| use proc_macro2::TokenStream; |
| use quote::{format_ident, quote}; |
| use syn::{ |
| parenthesized, |
| parse::{Parse, ParseStream}, |
| spanned::Spanned, |
| }; |
| |
| pub enum Forward { |
| Unnamed(usize), |
| Named(syn::Ident), |
| } |
| |
| impl Parse for Forward { |
| fn parse(input: ParseStream) -> syn::Result<Self> { |
| let forward = input.parse::<syn::Ident>()?; |
| if forward != "forward" { |
| return Err(syn::Error::new(forward.span(), "msg")); |
| } |
| let content; |
| parenthesized!(content in input); |
| let looky = content.lookahead1(); |
| if looky.peek(syn::LitInt) { |
| let int: syn::LitInt = content.parse()?; |
| let index = int.base10_parse()?; |
| return Ok(Forward::Unnamed(index)); |
| } |
| Ok(Forward::Named(content.parse()?)) |
| } |
| } |
| |
| #[derive(Copy, Clone)] |
| pub enum WhichFn { |
| Code, |
| Help, |
| Url, |
| Severity, |
| Labels, |
| SourceCode, |
| Related, |
| DiagnosticSource, |
| } |
| |
| impl WhichFn { |
| pub fn method_call(&self) -> TokenStream { |
| match self { |
| Self::Code => quote! { code() }, |
| Self::Help => quote! { help() }, |
| Self::Url => quote! { url() }, |
| Self::Severity => quote! { severity() }, |
| Self::Labels => quote! { labels() }, |
| Self::SourceCode => quote! { source_code() }, |
| Self::Related => quote! { related() }, |
| Self::DiagnosticSource => quote! { diagnostic_source() }, |
| } |
| } |
| |
| pub fn signature(&self) -> TokenStream { |
| match self { |
| Self::Code => quote! { |
| fn code(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> |
| }, |
| Self::Help => quote! { |
| fn help(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> |
| }, |
| Self::Url => quote! { |
| fn url(& self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> |
| }, |
| Self::Severity => quote! { |
| fn severity(&self) -> std::option::Option<miette::Severity> |
| }, |
| Self::Related => quote! { |
| fn related(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = &dyn miette::Diagnostic> + '_>> |
| }, |
| Self::Labels => quote! { |
| fn labels(&self) -> std::option::Option<std::boxed::Box<dyn std::iter::Iterator<Item = miette::LabeledSpan> + '_>> |
| }, |
| Self::SourceCode => quote! { |
| fn source_code(&self) -> std::option::Option<&dyn miette::SourceCode> |
| }, |
| Self::DiagnosticSource => quote! { |
| fn diagnostic_source(&self) -> std::option::Option<&dyn miette::Diagnostic> |
| }, |
| } |
| } |
| |
| pub fn catchall_arm(&self) -> TokenStream { |
| quote! { _ => std::option::Option::None } |
| } |
| } |
| |
| impl Forward { |
| pub fn for_transparent_field(fields: &syn::Fields) -> syn::Result<Self> { |
| let make_err = || { |
| syn::Error::new( |
| fields.span(), |
| "you can only use #[diagnostic(transparent)] with exactly one field", |
| ) |
| }; |
| match fields { |
| syn::Fields::Named(named) => { |
| let mut iter = named.named.iter(); |
| let field = iter.next().ok_or_else(make_err)?; |
| if iter.next().is_some() { |
| return Err(make_err()); |
| } |
| let field_name = field |
| .ident |
| .clone() |
| .unwrap_or_else(|| format_ident!("unnamed")); |
| Ok(Self::Named(field_name)) |
| } |
| syn::Fields::Unnamed(unnamed) => { |
| if unnamed.unnamed.iter().len() != 1 { |
| return Err(make_err()); |
| } |
| Ok(Self::Unnamed(0)) |
| } |
| _ => Err(syn::Error::new( |
| fields.span(), |
| "you cannot use #[diagnostic(transparent)] with a unit struct or a unit variant", |
| )), |
| } |
| } |
| |
| pub fn gen_struct_method(&self, which_fn: WhichFn) -> TokenStream { |
| let signature = which_fn.signature(); |
| let method_call = which_fn.method_call(); |
| |
| let field_name = match self { |
| Forward::Named(field_name) => quote!(#field_name), |
| Forward::Unnamed(index) => { |
| let index = syn::Index::from(*index); |
| quote!(#index) |
| } |
| }; |
| |
| quote! { |
| #[inline] |
| #signature { |
| self.#field_name.#method_call |
| } |
| } |
| } |
| |
| pub fn gen_enum_match_arm(&self, variant: &syn::Ident, which_fn: WhichFn) -> TokenStream { |
| let method_call = which_fn.method_call(); |
| match self { |
| Forward::Named(field_name) => quote! { |
| Self::#variant { #field_name, .. } => #field_name.#method_call, |
| }, |
| Forward::Unnamed(index) => { |
| let underscores: Vec<_> = core::iter::repeat(quote! { _, }).take(*index).collect(); |
| let unnamed = format_ident!("unnamed"); |
| quote! { |
| Self::#variant ( #(#underscores)* #unnamed, .. ) => #unnamed.#method_call, |
| } |
| } |
| } |
| } |
| } |