mirror of https://github.com/zkat/miette.git
162 lines
5.2 KiB
Rust
162 lines
5.2 KiB
Rust
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,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|