macro-input-macros 0.3.1

the macro part of macro-input
Documentation
use crate::{field_name, mod_name, DEFAULT_VALUE_FIELD, RENAME_FIELD};
use heck::SnekCase;
use macro_compose::{Collector, Context, Expand};
use macro_input_core::DefaultValue;
use std::convert::TryFrom;
use syn::{parse_quote, Data, DeriveInput, Expr, Field, ItemConst, ItemMod, Type};

pub struct ConstFields;

impl Expand<DeriveInput> for ConstFields {
    type Output = ItemMod;

    fn expand(&self, input: &DeriveInput, c: &mut Collector) -> Option<Self::Output> {
        let path = RENAME_FIELD
            .get_value::<Option<String>>(&input.attrs)
            .unwrap()
            .unwrap_or_else(|| input.ident.to_string().to_snek_case());

        let const_field_expand = ConstFieldExpand { path };

        let fields = match &input.data {
            Data::Struct(s) => &s.fields,
            _ => unreachable!(),
        };

        let const_fields = fields.iter().map(|f| {
            let mut subcontext = Context::new_by_ref(c, f);
            subcontext.capture(&const_field_expand)
        });

        let field_refs = fields.iter().map(|f| -> Expr {
            let (_, ident) = field_name(f);
            parse_quote!(#ident)
        });

        let mod_ident = mod_name(input);
        Some(parse_quote!(
            mod #mod_ident {
                #(#const_fields)*

                const FIELDS: &[&::macro_input::Def] = &[#(&#field_refs),*];
                pub const FIELD_DEFS: ::macro_input::Defs = ::macro_input::Defs::new(FIELDS);
            }
        ))
    }
}

struct ConstFieldExpand {
    path: String,
}

impl Expand<Field> for ConstFieldExpand {
    type Output = ItemConst;

    fn expand(&self, f: &Field, _: &mut Collector) -> Option<Self::Output> {
        fn is_optional(f: &Field) -> bool {
            if let Type::Path(tp) = &f.ty {
                tp.path.segments.len() == 1 && tp.path.segments.first().unwrap().ident == "Option"
            } else {
                false
            }
        }

        let (name, ident) = field_name(f);

        let default_value = DEFAULT_VALUE_FIELD.get_lit(&f.attrs).unwrap();
        let value = DefaultValue::from_lit(
            macro_input_core::Type::try_from(&f.ty).unwrap(),
            default_value.clone(),
        )
        .unwrap();

        let optional = is_optional(f) || default_value.is_some();
        let required = !optional;

        let path = &self.path;
        Some(parse_quote!(
            pub const #ident: ::macro_input::Def =
            ::macro_input::Def::new(#path, #name, #required, #value);
        ))
    }
}