Skip to content

Commit

Permalink
feat(help): allow non-option values in #[help] fields
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Apr 18, 2022
1 parent 23ee364 commit ea55f45
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 11 deletions.
30 changes: 19 additions & 11 deletions miette-derive/src/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{

pub enum Help {
Display(Display),
Field(syn::Member),
Field(syn::Member, Box<syn::Type>),
}

impl Parse for Help {
Expand Down Expand Up @@ -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()))));
}
}
}
Expand All @@ -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<dyn std::fmt::Display + 'a> { 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<dyn std::fmt::Display + 'a> { std::boxed::Box::new(format!("{}", #var)) })
},
})
}
}
Expand All @@ -126,13 +130,17 @@ impl Help {
}
})
}
Help::Field(member) => Some(quote! {
fn help<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + 'a>> {
#[allow(unused_variables, deprecated)]
let Self #display_pat = self;
self.#member.as_ref().map(|h| -> std::boxed::Box<dyn std::fmt::Display + 'a> { 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<std::boxed::Box<dyn std::fmt::Display + 'a>> {
#[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<dyn std::fmt::Display + 'a> { std::boxed::Box::new(format!("{}", #var)) })
}
})
}
}
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
32 changes: 32 additions & 0 deletions src/macro_helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#[doc(hidden)]
pub trait IsOption {}
impl <T> IsOption for Option<T> {}

#[doc(hidden)]
#[derive(Debug, Default)]
pub struct OptionalWrapper<T>(pub core::marker::PhantomData<T>);

impl<T> OptionalWrapper<T> {
pub fn new() -> Self {
Self(core::marker::PhantomData)
}
}

#[doc(hidden)]
pub trait ToOption {
#[doc(hidden)]
fn to_option<T>(self, value: T) -> Option<T>;
}

impl<T> OptionalWrapper<T> where T: IsOption {
#[doc(hidden)]
pub fn to_option(self, value: &T) -> &T {
value
}
}

impl<T> ToOption for &OptionalWrapper<T> {
fn to_option<U>(self, value: U) -> Option<U> {
Some(value)
}
}
10 changes: 10 additions & 0 deletions tests/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down

0 comments on commit ea55f45

Please sign in to comment.