diff --git a/miette-derive/src/lib.rs b/miette-derive/src/lib.rs index 709ed57..dce8a16 100644 --- a/miette-derive/src/lib.rs +++ b/miette-derive/src/lib.rs @@ -10,6 +10,7 @@ mod fmt; mod help; mod severity; mod snippets; +mod utils; #[proc_macro_derive(Diagnostic, attributes(diagnostic, snippet, highlight))] pub fn derive_diagnostic(input: proc_macro::TokenStream) -> proc_macro::TokenStream { diff --git a/miette-derive/src/severity.rs b/miette-derive/src/severity.rs index df9b562..ce1e7ba 100644 --- a/miette-derive/src/severity.rs +++ b/miette-derive/src/severity.rs @@ -1,4 +1,4 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{ parenthesized, @@ -8,7 +8,7 @@ use syn::{ use crate::diagnostic::DiagnosticVariant; -pub struct Severity(pub syn::Path); +pub struct Severity(pub syn::Ident); impl Parse for Severity { fn parse(input: ParseStream) -> syn::Result { @@ -21,20 +21,40 @@ impl Parse for Severity { let la = content.lookahead1(); if la.peek(syn::LitStr) { let str = content.parse::()?; - Ok(Severity(str.parse()?)) + let sev = get_severity(&str.value(), str.span())?; + Ok(Severity(syn::Ident::new(&sev, str.span()))) } else { - let path = content.parse::()?; - Ok(Severity(path)) + let ident = content.parse::()?; + let sev = get_severity(&ident.to_string(), ident.span())?; + Ok(Severity(syn::Ident::new(&sev, ident.span()))) } } else { input.parse::()?; - Ok(Severity(input.parse::()?.parse()?)) + let str = input.parse::()?; + let sev = get_severity(&str.value(), str.span())?; + Ok(Severity(syn::Ident::new(&sev, str.span()))) } } else { - Err(syn::Error::new(ident.span(), "not a severity level.")) + Err(syn::Error::new( + ident.span(), + "MIETTE BUG: not a severity option", + )) } } } + +fn get_severity(input: &str, span: Span) -> syn::Result { + match input.to_lowercase().as_ref() { + "error" | "err" => Ok("Error".into()), + "warning" | "warn" => Ok("Warning".into()), + "advice" | "adv" | "info" => Ok("Advice".into()), + _ => Err(syn::Error::new( + span, + "Invalid severity level. Only Error, Warning, and Advice are supported.", + )), + } +} + impl Severity { pub(crate) fn gen_enum(variants: &[DiagnosticVariant]) -> Option { let sev_pairs = variants @@ -42,10 +62,15 @@ impl Severity { .filter(|v| v.severity.is_some()) .map( |DiagnosticVariant { - ident, severity, .. + ident, severity, fields, .. }| { let severity = &severity.as_ref().unwrap().0; - quote! { Self::#ident => std::option::Option::Some(miette::Severity::#severity), } + let fields = match fields { + syn::Fields::Named(_) => quote! { { .. } }, + syn::Fields::Unnamed(_) => quote! { (..) }, + syn::Fields::Unit => quote!{}, + }; + quote! { Self::#ident #fields => std::option::Option::Some(miette::Severity::#severity), } }, ) .collect::>(); diff --git a/miette-derive/src/snippets.rs b/miette-derive/src/snippets.rs index 053bd3c..9957581 100644 --- a/miette-derive/src/snippets.rs +++ b/miette-derive/src/snippets.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use proc_macro2::TokenStream; -use quote::{format_ident, quote, ToTokens}; +use quote::{format_ident, quote}; use syn::{ parse::{Parse, ParseStream}, punctuated::Punctuated, @@ -10,6 +10,7 @@ use syn::{ }; use crate::diagnostic::DiagnosticVariant; +use crate::utils::MemberOrString; pub struct Snippets(Vec); @@ -33,37 +34,6 @@ struct HighlightAttr { snippet: syn::Member, } -enum MemberOrString { - Member(syn::Member), - String(syn::LitStr), -} - -impl ToTokens for MemberOrString { - fn to_tokens(&self, tokens: &mut TokenStream) { - use MemberOrString::*; - match self { - Member(member) => member.to_tokens(tokens), - String(string) => string.to_tokens(tokens), - } - } -} - -impl Parse for MemberOrString { - fn parse(input: ParseStream) -> syn::Result { - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Ident) || lookahead.peek(syn::LitInt) { - Ok(MemberOrString::Member(input.parse()?)) - } else if lookahead.peek(syn::LitStr) { - Ok(MemberOrString::String(input.parse()?)) - } else { - Err(syn::Error::new( - input.span(), - "Expected a string or a field reference.", - )) - } - } -} - impl Parse for SnippetAttr { fn parse(input: ParseStream) -> syn::Result { let punc = Punctuated::::parse_terminated(input)?; diff --git a/miette-derive/src/utils.rs b/miette-derive/src/utils.rs new file mode 100644 index 0000000..389d849 --- /dev/null +++ b/miette-derive/src/utils.rs @@ -0,0 +1,34 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::parse::{Parse, ParseStream}; + +pub(crate) enum MemberOrString { + Member(syn::Member), + String(syn::LitStr), +} + +impl ToTokens for MemberOrString { + fn to_tokens(&self, tokens: &mut TokenStream) { + use MemberOrString::*; + match self { + Member(member) => member.to_tokens(tokens), + String(string) => string.to_tokens(tokens), + } + } +} + +impl Parse for MemberOrString { + fn parse(input: ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Ident) || lookahead.peek(syn::LitInt) { + Ok(MemberOrString::Member(input.parse()?)) + } else if lookahead.peek(syn::LitStr) { + Ok(MemberOrString::String(input.parse()?)) + } else { + Err(syn::Error::new( + input.span(), + "Expected a string or a field reference.", + )) + } + } +} diff --git a/tests/derive.rs b/tests/derive.rs index 5d700a3..eaca501 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -92,7 +92,7 @@ fn path_code() { fn path_severity() { #[derive(Debug, Diagnostic, Error)] #[error("welp")] - #[diagnostic(code(foo::bar::baz), severity(Warning))] + #[diagnostic(code(foo::bar::baz), severity("warning"))] struct FooStruct; assert_eq!(Some(Severity::Warning), FooStruct.severity());