diff options
Diffstat (limited to '')
-rw-r--r-- | vendor/serde_repr/src/parse.rs | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/vendor/serde_repr/src/parse.rs b/vendor/serde_repr/src/parse.rs new file mode 100644 index 000000000..2c0ad1e7d --- /dev/null +++ b/vendor/serde_repr/src/parse.rs @@ -0,0 +1,114 @@ +use proc_macro2::Span; +use syn::parse::{Error, Parse, ParseStream, Parser, Result}; +use syn::{parenthesized, Data, DeriveInput, Fields, Ident, Meta, NestedMeta}; + +pub struct Input { + pub ident: Ident, + pub repr: Ident, + pub variants: Vec<Variant>, + pub default_variant: Option<Variant>, +} + +#[derive(Clone)] +pub struct Variant { + pub ident: Ident, + pub attrs: VariantAttrs, +} + +#[derive(Clone)] +pub struct VariantAttrs { + pub is_default: bool, +} + +fn parse_meta(attrs: &mut VariantAttrs, meta: &Meta) { + if let Meta::List(value) = meta { + for meta in &value.nested { + if let NestedMeta::Meta(Meta::Path(path)) = meta { + if path.is_ident("other") { + attrs.is_default = true; + } + } + } + } +} + +fn parse_attrs(variant: &syn::Variant) -> Result<VariantAttrs> { + let mut attrs = VariantAttrs { is_default: false }; + for attr in &variant.attrs { + if attr.path.is_ident("serde") { + parse_meta(&mut attrs, &attr.parse_meta()?); + } + } + Ok(attrs) +} + +impl Parse for Input { + fn parse(input: ParseStream) -> Result<Self> { + let call_site = Span::call_site(); + let derive_input = DeriveInput::parse(input)?; + + let data = match derive_input.data { + Data::Enum(data) => data, + _ => { + return Err(Error::new(call_site, "input must be an enum")); + } + }; + + let variants = data + .variants + .into_iter() + .map(|variant| match variant.fields { + Fields::Unit => { + let attrs = parse_attrs(&variant)?; + Ok(Variant { + ident: variant.ident, + attrs, + }) + } + Fields::Named(_) | Fields::Unnamed(_) => { + Err(Error::new(variant.ident.span(), "must be a unit variant")) + } + }) + .collect::<Result<Vec<Variant>>>()?; + + if variants.is_empty() { + return Err(Error::new(call_site, "there must be at least one variant")); + } + + let generics = derive_input.generics; + if !generics.params.is_empty() || generics.where_clause.is_some() { + return Err(Error::new(call_site, "generic enum is not supported")); + } + + let mut repr = None; + for attr in derive_input.attrs { + if attr.path.is_ident("repr") { + fn repr_arg(input: ParseStream) -> Result<Ident> { + let content; + parenthesized!(content in input); + content.parse() + } + let ty = repr_arg.parse2(attr.tokens)?; + repr = Some(ty); + break; + } + } + let repr = repr.ok_or_else(|| Error::new(call_site, "missing #[repr(...)] attribute"))?; + + let mut default_variants = variants.iter().filter(|x| x.attrs.is_default); + let default_variant = default_variants.next().cloned(); + if default_variants.next().is_some() { + return Err(Error::new( + call_site, + "only one variant can be #[serde(other)]", + )); + } + + Ok(Input { + ident: derive_input.ident, + repr, + variants, + default_variant, + }) + } +} |