Repository: zkat/miette (rust)

Issue:
`#[source]` does not work on `Box<dyn Error + Send + Sync>`
In order to get miette (with thiserror) to render the full error chain I need to be able to add the `#[source]` macro onto the "source" error.

In my project I'm using a boxed error that also needs to be `Send + Sync` like so:

```rust
#[derive(Diagnostic, Error)]
struct MyError {
  #[error("Any Error")]
  #[diagnostic(transparent)]
  AnyError(#[source] Box<dyn Diagnostic + Send + Sync>)
}
```

However that example fails with the following compilation error:

```
error[E0599]: the method `as_dyn_error` exists for reference `&Box<dyn Diagnostic + Send + Sync>`, but its trait bounds were not satisfied

20  |   pub trait Diagnostic: std::error::Error {
    |   ---------------------------------------
    |   |
    |   doesn't satisfy `dyn Diagnostic + Send + Sync: AsDynError<'_>`
    |   doesn't satisfy `dyn Diagnostic + Send + Sync: Sized`
```

I was able to get around the issue by using an `Arc` instead of a `Box` - but I don't need any `Arc` features so it would be great if it worked with `Box`.

Pull request description:
fix(protocol): add StdError impl for Box<dyn Diagnostic + Send + Sync>
Adds `std::error::Error` impls for `Box<dyn Diagnostic + Send + Sync>` (and other variants).

Fixes #272.

Patch:
diff --git a/README.md b/README.md
index ff25b3a..8375876 100644
--- a/README.md
+++ b/README.md
@@ -96,7 +96,7 @@ You can derive a `Diagnostic` from any `std::error::Error` type.
 
 `thiserror` is a great way to define them, and plays nicely with `miette`!
 */
-use miette::{Diagnostic, SourceSpan};
+use miette::{Diagnostic, NamedSource, Result, SourceSpan};
 use thiserror::Error;
 
 #[derive(Error, Debug, Diagnostic)]
@@ -123,12 +123,10 @@ Use this `Result` type (or its expanded version) as the return type
 throughout your app (but NOT your libraries! Those should always return
 concrete types!).
 */
-use miette::{NamedSource, Result};
 fn this_fails() -> Result<()> {
     // You can use plain strings as a `Source`, or anything that implements
     // the one-method `Source` trait.
     let src = "source\n  text\n    here".to_string();
-    let len = src.len();
 
     Err(MyBad {
         src: NamedSource::new("bad_file.rs", src),
@@ -648,7 +646,7 @@ under the Apache License. Some code is taken from
 [`MietteHandler`]: https://docs.rs/miette/latest/miette/struct.MietteHandler.html
 [`MietteDiagnostic`]: https://docs.rs/miette/latest/miette/struct.MietteDiagnostic.html
 [`Report`]: https://docs.rs/miette/latest/miette/struct.Report.html
-[`ReportHandler`]: https://docs.rs/miette/latest/miette/struct.ReportHandler.html
+[`ReportHandler`]: https://docs.rs/miette/latest/miette/trait.ReportHandler.html
 [`Result`]: https://docs.rs/miette/latest/miette/type.Result.html
-[`SourceCode`]: https://docs.rs/miette/latest/miette/struct.SourceCode.html
+[`SourceCode`]: https://docs.rs/miette/latest/miette/trait.SourceCode.html
 [`SourceSpan`]: https://docs.rs/miette/latest/miette/struct.SourceSpan.html
diff --git a/README.tpl b/README.tpl
index d598eb8..f1126f1 100644
--- a/README.tpl
+++ b/README.tpl
@@ -12,7 +12,7 @@
 [`MietteHandler`]: https://docs.rs/miette/latest/miette/struct.MietteHandler.html
 [`MietteDiagnostic`]: https://docs.rs/miette/latest/miette/struct.MietteDiagnostic.html
 [`Report`]: https://docs.rs/miette/latest/miette/struct.Report.html
-[`ReportHandler`]: https://docs.rs/miette/latest/miette/struct.ReportHandler.html
+[`ReportHandler`]: https://docs.rs/miette/latest/miette/trait.ReportHandler.html
 [`Result`]: https://docs.rs/miette/latest/miette/type.Result.html
-[`SourceCode`]: https://docs.rs/miette/latest/miette/struct.SourceCode.html
+[`SourceCode`]: https://docs.rs/miette/latest/miette/trait.SourceCode.html
 [`SourceSpan`]: https://docs.rs/miette/latest/miette/struct.SourceSpan.html
diff --git a/src/protocol.rs b/src/protocol.rs
index 7531db1..f516984 100644
--- a/src/protocol.rs
+++ b/src/protocol.rs
@@ -69,14 +69,26 @@ pub trait Diagnostic: std::error::Error {
     }
 }
 
-impl std::error::Error for Box<dyn Diagnostic> {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        (**self).source()
+macro_rules! box_impls {
+    ($($box_type:ty),*) => {
+        $(
+            impl std::error::Error for $box_type {
+                fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+                    (**self).source()
+                }
+
+                fn cause(&self) -> Option<&dyn std::error::Error> {
+                    self.source()
+                }
+            }
+        )*
     }
+}
 
-    fn cause(&self) -> Option<&dyn std::error::Error> {
-        self.source()
-    }
+box_impls! {
+    Box<dyn Diagnostic>,
+    Box<dyn Diagnostic + Send>,
+    Box<dyn Diagnostic + Send + Sync>
 }
 
 impl<T: Diagnostic + Send + Sync + 'static> From<T>


Test patch:
diff --git a/tests/test_derive_source_chain.rs b/tests/test_derive_source_chain.rs
new file mode 100644
index 0000000..fb7ddb2
--- /dev/null
+++ b/tests/test_derive_source_chain.rs
@@ -0,0 +1,69 @@
+use std::sync::Arc;
+
+use miette::{miette, Diagnostic};
+use thiserror::Error;
+
+#[test]
+fn test_source() {
+    #[derive(Debug, Diagnostic, Error)]
+    #[error("Bar")]
+    struct Bar;
+
+    #[derive(Debug, Diagnostic, Error)]
+    #[error("Foo")]
+    struct Foo {
+        #[source]
+        bar: Bar,
+    }
+
+    let e = miette!(Foo { bar: Bar });
+    let mut chain = e.chain();
+
+    assert_eq!("Foo", chain.next().unwrap().to_string());
+    assert_eq!("Bar", chain.next().unwrap().to_string());
+    assert!(chain.next().is_none());
+}
+
+#[test]
+fn test_source_boxed() {
+    #[derive(Debug, Diagnostic, Error)]
+    #[error("Bar")]
+    struct Bar;
+
+    #[derive(Debug, Diagnostic, Error)]
+    #[error("Foo")]
+    struct Foo {
+        #[source]
+        bar: Box<dyn Diagnostic + Send + Sync>,
+    }
+
+    let error = miette!(Foo { bar: Box::new(Bar) });
+
+    let mut chain = error.chain();
+
+    assert_eq!("Foo", chain.next().unwrap().to_string());
+    assert_eq!("Bar", chain.next().unwrap().to_string());
+    assert!(chain.next().is_none());
+}
+
+#[test]
+fn test_source_arc() {
+    #[derive(Debug, Diagnostic, Error)]
+    #[error("Bar")]
+    struct Bar;
+
+    #[derive(Debug, Diagnostic, Error)]
+    #[error("Foo")]
+    struct Foo {
+        #[source]
+        bar: Arc<dyn Diagnostic + Send + Sync>,
+    }
+
+    let error = miette!(Foo { bar: Arc::new(Bar) });
+
+    let mut chain = error.chain();
+
+    assert_eq!("Foo", chain.next().unwrap().to_string());
+    assert_eq!("Bar", chain.next().unwrap().to_string());
+    assert!(chain.next().is_none());
+}