Repository: zkat/miette (rust)

Issue:
Use `Report` in `related`
Currently you can't put `Vec<Report>` into the `related` field, only concrete error. This is quite inconvenient in certain cases.

One option would be to make some way to agument `related`:
```rust
struct MultiError {
  #[related(Report)]
  related: Vec<Report>,
}
```
Or maybe more generic:
```rust
struct MultiError {
  #[related(AsRef)] // or #[related(as_ref)]
  related: Vec<Report>,
}
```
Or even more generic:
```rust
struct MultiError {
  #[related(|x| x.as_ref())]
  related: Vec<Report>,
}
```

It should also be possible to use [dtolnay's hack](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md). But I'm not sure how much complexity that adds to the derive.

What do you think?


Pull request description:
feat(derive): allow `Report` in `related`
Fixes #119

I'm not sure if using `borrow()` should be thought of as a great way to handle the issue or it's a hack.

The alternative would be to make some private trait that we have to expose using `docs(hidden)` for derive to use. But this may also make error reporting confusing (i.e. failing with trait name that can't be found anywhere in the docs).

I think `Borrow` is safe to implement for the `Report`. I had a tiny concern that it make prohibit using error types with (non-static) lifetimes in `related`. But I think it's *not the case*. Although, I could not test that as code-gen for nonstatic lifetimes in `related` is untested and broken for now.

Patch:
diff --git a/miette-derive/src/related.rs b/miette-derive/src/related.rs
index b81df5f..5533f2d 100644
--- a/miette-derive/src/related.rs
+++ b/miette-derive/src/related.rs
@@ -69,7 +69,10 @@ impl Related {
         let rel = &self.0;
         Some(quote! {
             fn related<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
-                std::option::Option::Some(std::boxed::Box::new(self.#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x })))
+                use ::core::borrow::Borrow;
+                std::option::Option::Some(std::boxed::Box::new(
+                        self.#rel.iter().map(|x| -> &(dyn miette::Diagnostic) { &*x.borrow() })
+                ))
             }
         })
     }
diff --git a/src/eyreish/error.rs b/src/eyreish/error.rs
index 0f45dc9..bb31d59 100644
--- a/src/eyreish/error.rs
+++ b/src/eyreish/error.rs
@@ -727,3 +727,9 @@ impl AsRef<dyn Diagnostic> for Report {
         &**self
     }
 }
+
+impl std::borrow::Borrow<dyn Diagnostic> for Report {
+    fn borrow(&self) -> &(dyn Diagnostic + 'static) {
+        self.as_ref()
+    }
+}


Test patch:
diff --git a/tests/derive.rs b/tests/derive.rs
index f1767c4..60a7763 100644
--- a/tests/derive.rs
+++ b/tests/derive.rs
@@ -1,4 +1,4 @@
-use miette::{Diagnostic, Severity, SourceSpan};
+use miette::{Diagnostic, Report, Severity, SourceSpan};
 use thiserror::Error;
 
 #[test]
@@ -32,6 +32,17 @@ fn related() {
     struct Baz;
 }
 
+#[test]
+fn related_report() {
+    #[derive(Error, Debug, Diagnostic)]
+    #[error("welp")]
+    #[diagnostic(code(foo::bar::baz))]
+    struct Foo {
+        #[related]
+        related: Vec<Report>,
+    }
+}
+
 #[test]
 fn basic_struct() {
     #[derive(Debug, Diagnostic, Error)]