diff options
Diffstat (limited to 'vendor/miette-derive/src/utils.rs')
-rw-r--r-- | vendor/miette-derive/src/utils.rs | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/vendor/miette-derive/src/utils.rs b/vendor/miette-derive/src/utils.rs new file mode 100644 index 0000000..b867849 --- /dev/null +++ b/vendor/miette-derive/src/utils.rs @@ -0,0 +1,140 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use syn::{ + parse::{Parse, ParseStream}, + spanned::Spanned, +}; + +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<Self> { + 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.", + )) + } + } +} + +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + forward::WhichFn, +}; + +pub(crate) fn gen_all_variants_with( + variants: &[DiagnosticDef], + which_fn: WhichFn, + mut f: impl FnMut(&syn::Ident, &syn::Fields, &DiagnosticConcreteArgs) -> Option<TokenStream>, +) -> Option<TokenStream> { + let pairs = variants + .iter() + .filter_map(|def| { + def.args + .forward_or_override_enum(&def.ident, which_fn, |concrete| { + f(&def.ident, &def.fields, concrete) + }) + }) + .collect::<Vec<_>>(); + if pairs.is_empty() { + return None; + } + let signature = which_fn.signature(); + let catchall = which_fn.catchall_arm(); + Some(quote! { + #signature { + #[allow(unused_variables, deprecated)] + match self { + #(#pairs)* + #catchall + } + } + }) +} + +use crate::fmt::Display; +use std::collections::HashSet; + +pub(crate) fn gen_unused_pat(fields: &syn::Fields) -> TokenStream { + match fields { + syn::Fields::Named(_) => quote! { { .. } }, + syn::Fields::Unnamed(_) => quote! { ( .. ) }, + syn::Fields::Unit => quote! {}, + } +} + +/// Goes in the slot `let Self #pat = self;` or `match self { Self #pat => ... +/// }`. +fn gen_fields_pat(fields: &syn::Fields) -> TokenStream { + let member_idents = fields.iter().enumerate().map(|(i, field)| { + field + .ident + .as_ref() + .cloned() + .unwrap_or_else(|| format_ident!("_{}", i)) + }); + match fields { + syn::Fields::Named(_) => quote! { + { #(#member_idents),* } + }, + syn::Fields::Unnamed(_) => quote! { + ( #(#member_idents),* ) + }, + syn::Fields::Unit => quote! {}, + } +} + +/// The returned tokens go in the slot `let Self #pat = self;` or `match self { +/// Self #pat => ... }`. The members can be passed to +/// `Display::expand_shorthand[_cloned]`. +pub(crate) fn display_pat_members(fields: &syn::Fields) -> (TokenStream, HashSet<syn::Member>) { + let pat = gen_fields_pat(fields); + 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(); + (pat, members) +} + +impl Display { + /// Returns `(fmt, args)` which must be passed to some kind of format macro + /// without tokens in between, i.e. `format!(#fmt #args)`. + pub(crate) fn expand_shorthand_cloned( + &self, + members: &HashSet<syn::Member>, + ) -> (syn::LitStr, TokenStream) { + let mut display = self.clone(); + display.expand_shorthand(members); + let Display { fmt, args, .. } = display; + (fmt, args) + } +} |