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, pub default_variant: Option, } #[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 { 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 { 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::>>()?; 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 { 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, }) } }