mirror of https://github.com/zkat/miette.git
159 lines
5.7 KiB
Rust
159 lines
5.7 KiB
Rust
use std::collections::HashSet;
|
|
|
|
use proc_macro2::TokenStream;
|
|
use quote::{format_ident, quote};
|
|
use syn::{
|
|
parenthesized,
|
|
parse::{Parse, ParseStream},
|
|
spanned::Spanned,
|
|
Fields, Token,
|
|
};
|
|
|
|
use crate::diagnostic::DiagnosticVariant;
|
|
use crate::fmt::{self, Display};
|
|
|
|
pub struct Help {
|
|
pub display: Display,
|
|
}
|
|
|
|
impl Parse for Help {
|
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
|
let ident = input.parse::<syn::Ident>()?;
|
|
if ident == "help" {
|
|
let la = input.lookahead1();
|
|
if la.peek(syn::token::Paren) {
|
|
let content;
|
|
parenthesized!(content in input);
|
|
let fmt = content.parse()?;
|
|
let args = if content.is_empty() {
|
|
TokenStream::new()
|
|
} else {
|
|
content.parse::<Token![,]>()?;
|
|
fmt::parse_token_expr(&content, false)?
|
|
};
|
|
let display = Display {
|
|
fmt,
|
|
args,
|
|
has_bonus_display: false,
|
|
};
|
|
Ok(Help { display })
|
|
} else {
|
|
input.parse::<Token![=]>()?;
|
|
Ok(Help {
|
|
display: Display {
|
|
fmt: input.parse()?,
|
|
args: TokenStream::new(),
|
|
has_bonus_display: false,
|
|
},
|
|
})
|
|
}
|
|
} else {
|
|
Err(syn::Error::new(ident.span(), "not a help"))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Help {
|
|
pub(crate) fn gen_enum(variants: &[DiagnosticVariant]) -> Option<TokenStream> {
|
|
let help_pairs = variants
|
|
.iter()
|
|
.filter(|v| v.help.is_some())
|
|
.map(
|
|
|DiagnosticVariant {
|
|
ref ident,
|
|
ref help,
|
|
ref fields,
|
|
..
|
|
}| {
|
|
let mut display = help.as_ref().expect("already checked for Some").display.clone();
|
|
let member_idents = fields.iter().enumerate().map(|(i, field)| {
|
|
field
|
|
.ident
|
|
.as_ref()
|
|
.cloned()
|
|
.unwrap_or_else(|| format_ident!("_{}", i))
|
|
});
|
|
let members: HashSet<syn::Member> = fields.iter().enumerate().map(|(i, field)| {
|
|
if let Some(ident) = field.ident.as_ref().cloned() {
|
|
syn::Member::Named(ident)
|
|
} else {
|
|
syn::Member::Unnamed(syn::Index { index: i as u32, span: field.span() })
|
|
}
|
|
}).collect();
|
|
display.expand_shorthand(&members);
|
|
let Display { fmt, args, .. } = display;
|
|
match fields {
|
|
syn::Fields::Named(_) => {
|
|
quote! { Self::#ident{ #(#member_idents),* } => std::option::Option::Some(std::boxed::Box::new(format!(#fmt, #args))), }
|
|
}
|
|
syn::Fields::Unnamed(_) => {
|
|
quote! { Self::#ident( #(#member_idents),* ) => std::option::Option::Some(std::boxed::Box::new(format!(#fmt, #args))), }
|
|
}
|
|
syn::Fields::Unit =>
|
|
quote! { Self::#ident => std::option::Option::Some(std::boxed::Box::new(format!(#fmt, #args))), },
|
|
}
|
|
},
|
|
)
|
|
.collect::<Vec<_>>();
|
|
if help_pairs.is_empty() {
|
|
None
|
|
} else {
|
|
Some(quote! {
|
|
fn help<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + 'a>> {
|
|
#[allow(unused_variables, deprecated)]
|
|
match self {
|
|
#(#help_pairs)*
|
|
_ => None,
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
pub(crate) fn gen_struct(&self, fields: &Fields) -> Option<TokenStream> {
|
|
let mut display = self.display.clone();
|
|
let members: HashSet<syn::Member> = fields
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, field)| {
|
|
if let Some(ident) = field.ident.as_ref().cloned() {
|
|
syn::Member::Named(ident)
|
|
} else {
|
|
syn::Member::Unnamed(syn::Index {
|
|
index: i as u32,
|
|
span: field.span(),
|
|
})
|
|
}
|
|
})
|
|
.collect();
|
|
display.expand_shorthand(&members);
|
|
let members = members.iter();
|
|
let Display { fmt, args, .. } = display;
|
|
let fields_pat = match fields {
|
|
Fields::Named(_) => quote! {
|
|
let Self { #(#members),* } = self;
|
|
},
|
|
Fields::Unnamed(_) => {
|
|
let vars = members.map(|member| {
|
|
if let syn::Member::Unnamed(member) = member {
|
|
format_ident!("_{}", member)
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
});
|
|
quote! {
|
|
let Self(#(#vars),*) = self;
|
|
}
|
|
}
|
|
Fields::Unit => quote! {},
|
|
};
|
|
Some(quote! {
|
|
fn help<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + 'a>> {
|
|
#[allow(unused_variables, deprecated)]
|
|
#fields_pat
|
|
std::option::Option::Some(std::boxed::Box::new(format!(#fmt, #args)))
|
|
}
|
|
})
|
|
}
|
|
}
|