diff --git a/miette-derive/src/help.rs b/miette-derive/src/help.rs index 1bbd51e..c5eedd1 100644 --- a/miette-derive/src/help.rs +++ b/miette-derive/src/help.rs @@ -18,7 +18,7 @@ use crate::{ pub enum Help { Display(Display), - Field(syn::Member), + Field(syn::Member, Box), } impl Parse for Help { @@ -78,7 +78,7 @@ impl Help { span: field.span(), }) }; - return Ok(Some(Help::Field(help))); + return Ok(Some(Help::Field(help, Box::new(field.ty.clone())))); } } } @@ -97,15 +97,19 @@ impl Help { Self::#ident #display_pat => std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))), }) } - Help::Field(member) => { + Help::Field(member, ty) => { let help = match &member { syn::Member::Named(ident) => ident.clone(), syn::Member::Unnamed(syn::Index { index, .. }) => { format_ident!("_{}", index) } }; + let var = quote! { __miette_internal_var }; Some(quote! { - Self::#ident #display_pat => #help.as_ref().map(|h| -> std::boxed::Box { std::boxed::Box::new(format!("{}", h)) }), + Self::#ident #display_pat => { + use miette::macro_helpers::ToOption; + miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&#help).as_ref().map(|#var| -> std::boxed::Box { std::boxed::Box::new(format!("{}", #var)) }) + }, }) } } @@ -126,13 +130,17 @@ impl Help { } }) } - Help::Field(member) => Some(quote! { - fn help<'a>(&'a self) -> std::option::Option> { - #[allow(unused_variables, deprecated)] - let Self #display_pat = self; - self.#member.as_ref().map(|h| -> std::boxed::Box { std::boxed::Box::new(format!("{}", h)) }) - } - }), + Help::Field(member, ty) => { + let var = quote! { __miette_internal_var }; + Some(quote! { + fn help<'a>(&'a self) -> std::option::Option> { + #[allow(unused_variables, deprecated)] + let Self #display_pat = self; + use miette::macro_helpers::ToOption; + miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#member).as_ref().map(|#var| -> std::boxed::Box { std::boxed::Box::new(format!("{}", #var)) }) + } + }) + } } } } diff --git a/src/lib.rs b/src/lib.rs index af26647..95d9d42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -589,6 +589,8 @@ mod eyreish; #[cfg(feature = "fancy-no-backtrace")] mod handler; mod handlers; +#[doc(hidden)] +pub mod macro_helpers; mod named_source; #[cfg(feature = "fancy")] mod panic; diff --git a/src/macro_helpers.rs b/src/macro_helpers.rs new file mode 100644 index 0000000..39a3960 --- /dev/null +++ b/src/macro_helpers.rs @@ -0,0 +1,32 @@ +#[doc(hidden)] +pub trait IsOption {} +impl IsOption for Option {} + +#[doc(hidden)] +#[derive(Debug, Default)] +pub struct OptionalWrapper(pub core::marker::PhantomData); + +impl OptionalWrapper { + pub fn new() -> Self { + Self(core::marker::PhantomData) + } +} + +#[doc(hidden)] +pub trait ToOption { + #[doc(hidden)] + fn to_option(self, value: T) -> Option; +} + +impl OptionalWrapper where T: IsOption { + #[doc(hidden)] + pub fn to_option(self, value: &T) -> &T { + value + } +} + +impl ToOption for &OptionalWrapper { + fn to_option(self, value: U) -> Option { + Some(value) + } +} diff --git a/tests/derive.rs b/tests/derive.rs index 1ee1bfa..6394abd 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -292,6 +292,16 @@ fn help_field() { "x".to_string(), Baz(Some("x".into())).help().unwrap().to_string() ); + + #[derive(Debug, Diagnostic, Error)] + #[error("welp")] + #[diagnostic()] + struct Quux(#[help] String); + + assert_eq!( + "x".to_string(), + Quux("x".into()).help().unwrap().to_string() + ); } #[test]