diff --git a/crates/pmp-emitter/crates/pmp-emitter-core/src/lib.rs b/crates/pmp-emitter/crates/pmp-emitter-core/src/lib.rs index a969019..0091f5c 100644 --- a/crates/pmp-emitter/crates/pmp-emitter-core/src/lib.rs +++ b/crates/pmp-emitter/crates/pmp-emitter-core/src/lib.rs @@ -29,6 +29,12 @@ struct StrictBool(bool); impl FromMeta for StrictBool { + fn from_word() -> Result { + Err(Error::custom( + "expected `= true/false`, provide a boolean value" + )) + } + fn from_value(value: &Lit) -> Result { match value { @@ -36,22 +42,59 @@ impl FromMeta for StrictBool _ => Err(Error::unexpected_lit_type(value)), } } +} - // intentionally NOT implementing from_word() - bare `exit` is rejected +/// A string that must be written as `= "..."` +#[derive(Debug, Clone)] +struct StrictString(String); + +impl FromMeta for StrictString +{ + fn from_word() -> Result { + Err(Error::custom( + "expected `= \"...\"`, provide a string value" + )) + } + + fn from_value(value: &Lit) -> Result { + String::from_value(value).map(StrictString) + } +} + +/// A path that must be written as `= my_fn` +#[derive(Debug, Clone)] +struct StrictPath(Path); + +impl FromMeta for StrictPath +{ + fn from_word() -> Result { + Err(Error::custom( + "expected `= my_fn`, provide a function path" + )) + } + + fn from_value(value: &Lit) -> Result { + Path::from_value(value).map(StrictPath) + } + + fn from_expr(expr: &syn::Expr) -> Result { + Path::from_expr(expr).map(StrictPath) + } } // ---------------------------------------------------------------------------------------------- // /// - `descriptor = "..."` - prefix shown before all variant messages (default: enum name) /// - `exit = true|false` - whether to call process::exit (default: false) +/// - `colorizer = fn` - fn(String) -> String applied to all variants unless overridden #[derive(Debug, Default, FromMeta)] struct EnumAttrs { #[darling(default)] exit: Option, #[darling(default)] - colorizer: Option, + colorizer: Option, #[darling(default)] - descriptor: Option, + descriptor: Option, } /// - `message = "..."` - error message (default: variant name as title case) @@ -62,9 +105,9 @@ struct VariantAttrs { #[darling(default)] exit: Option, #[darling(default)] - message: Option, + message: Option, #[darling(default)] - colorizer: Option, + colorizer: Option, } // ---------------------------------------------------------------------------------------------- // @@ -106,8 +149,10 @@ fn expand(shape: EmitterInput, raw_input: &DeriveInput) -> syn::Result syn::Result = None; + let mut errors: Option = None; let mut match_arms: Vec = Vec::with_capacity(variants.len()); for v in &variants @@ -128,7 +173,8 @@ fn expand(shape: EmitterInput, raw_input: &DeriveInput) -> syn::Result a, - Err(e) => { + Err(e) => + { match &mut errors { None => errors = Some(e), Some(existing) => existing.combine(e), @@ -137,7 +183,7 @@ fn expand(shape: EmitterInput, raw_input: &DeriveInput) -> syn::Result syn::Result quote! { #path(#header) }, - None => quote! { #header.to_string() }, + Some(StrictPath(path)) => quote! { #path(#header) }, + None => quote! { #header.to_string() }, }; let exit_tokens = if should_exit { quote! { std::process::exit(1); } } else { quote! {} }; @@ -158,14 +204,21 @@ fn expand(shape: EmitterInput, raw_input: &DeriveInput) -> syn::Result quote_spanned! { var_ident.span() => - #enum_ident::#var_ident(trace) => { - println!("{}\n\n{}", #formatted_header, trace); - #exit_tokens + ast::Style::Tuple => + { + let pattern = if v.fields.len() == 1 { quote! { (trace) } } else { quote! { (trace, ..) } }; + + quote_spanned! { var_ident.span() => + #enum_ident::#var_ident #pattern => + { + println!("{}\n\n{}", #formatted_header, trace); + #exit_tokens + } } }, _ => quote_spanned! { var_ident.span() => - #enum_ident::#var_ident => { + #enum_ident::#var_ident => + { println!("{}", #formatted_header); #exit_tokens }