feat(help): allow non-option values in #[help] fields

This commit is contained in:
Kat Marchán 2022-04-17 21:48:39 -07:00
parent 23ee3642d1
commit ea55f458fa
4 changed files with 63 additions and 11 deletions

View File

@ -18,7 +18,7 @@ use crate::{
pub enum Help {
Display(Display),
Field(syn::Member),
Field(syn::Member, Box<syn::Type>),
}
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<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)) })
},
})
}
}
@ -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)) })
}
})
}
}
}
}

View File

@ -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;

32
src/macro_helpers.rs Normal file
View File

@ -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)
}
}

View File

@ -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]