todel_codegen 0.3.3

Codegen crate for todel
Documentation
use syn::{spanned::Spanned, Error, Fields, ItemEnum, Lit, Meta, NestedMeta, Variant};

use super::{
    models::{EnumInfo, EnumVariant, Item},
    utils::{get_doc, get_field_infos, get_type},
};

pub fn handle_enum(_: &[NestedMeta], item: ItemEnum) -> Result<Item, Error> {
    let mut rename_all = None;
    let mut tag = None;
    let mut untagged = false;
    let mut content = None;

    for attr in item.attrs.iter().filter(|a| a.path.is_ident("serde")) {
        if let Ok(Meta::List(meta)) = attr.parse_meta() {
            for meta in meta.nested {
                match meta {
                    NestedMeta::Meta(Meta::NameValue(meta)) => {
                        if let Some(ident) = meta.path.get_ident() {
                            match ident.to_string().as_str() {
                                "rename_all" => {
                                    if let Lit::Str(lit) = meta.lit {
                                        rename_all = Some(lit.value());
                                    }
                                }
                                "tag" => {
                                    if let Lit::Str(lit) = meta.lit {
                                        tag = Some(lit.value());
                                    }
                                }
                                "content" => {
                                    if let Lit::Str(lit) = meta.lit {
                                        content = Some(lit.value());
                                    }
                                }
                                _ => {}
                            }
                        }
                    }
                    NestedMeta::Meta(Meta::Path(path)) => {
                        if path.is_ident("untagged") {
                            untagged = true;
                        }
                    }
                    _ => {}
                }
            }
        }
    }

    let mut variants = vec![];
    for variant in item.variants {
        variants.push(get_variant(variant)?);
    }

    Ok(Item::Enum(EnumInfo {
        content,
        tag,
        untagged,
        rename_all,
        variants,
    }))
}
fn get_variant(variant: Variant) -> Result<EnumVariant, syn::Error> {
    let doc = get_doc(&variant.attrs)?;
    let name = variant.ident.to_string();
    Ok(match variant.fields {
        Fields::Unit => EnumVariant::Unit { name, doc },
        Fields::Unnamed(fields) => {
            if fields.unnamed.len() > 1 {
                return Err(Error::new(
                    fields.span(),
                    "Cannot document tuple enum variants with more than one field",
                ));
            }
            let field = fields.unnamed.first().ok_or_else(|| {
                Error::new(
                    fields.span(),
                    "Tuple enum variants must have at least one field",
                )
            })?;
            EnumVariant::Tuple {
                name,
                doc,
                field_type: get_type(&field.ty)?,
            }
        }
        Fields::Named(struct_fields) => EnumVariant::Struct {
            name,
            doc,
            fields: get_field_infos(struct_fields.named.iter())?,
        },
    })
}