fn derive_options_impl()

in rust/ccommon-derive/src/lib.rs [236:352]


fn derive_options_impl(input: DeriveInput) -> Result<proc_macro2::TokenStream, Error> {
    let krate = crate_name("ccommon-rs")?;

    let data = match input.data {
        Data::Struct(data) => data,
        Data::Enum(data) => {
            return Err(Error::new(
                data.enum_token.span(),
                "Can only derive Metrics for a struct",
            ))
        }
        Data::Union(data) => {
            return Err(Error::new(
                data.union_token.span(),
                "Can only derive Metrics for a struct",
            ))
        }
    };

    if !is_repr_c_or_transparent(&input.attrs) {
        return Err(Error::new(
            input.ident.span(),
            format!(
                "`{}` must be either #[repr(C)] or #[repr(transparent)] to implement Metrics",
                input.ident
            ),
        ));
    }

    if has_generics(&input.generics) {
        return Err(Error::new(
            input.generics.span(),
            "Cannot derive Metrics for a struct with generics",
        ));
    }

    let ident = input.ident;
    let process_field = |is_tuple| {
        let krate = krate.clone();
        move |(i, field): (usize, &Field)| {
            let ty = &field.ty;
            let name = &field.ident;
            let label = if is_tuple {
                quote! {}
            } else {
                quote! { #name: }
            };

            Ok(match get_option_attr(&field.attrs)? {
                Some(attr) => {
                    let desc = to_c_str(&attr.desc.val);
                    let namestr = match attr.name {
                        Some(name) => to_c_str(&name.val),
                        None => match field.ident.as_ref() {
                            Some(name) => to_c_str(&name_as_lit(name)),
                            None => {
                                to_c_str(&Lit::Str(LitStr::new(&format!("{}", i), field.span())))
                            }
                        },
                    };

                    match attr.default.map(|x| x.val) {
                        Some(default) => quote! {
                            #label <#ty as #krate::option::SingleOption>::new(
                                #default,
                                #namestr,
                                #desc
                            )
                        },
                        None => quote! {
                            #label <#ty as #krate::option::SingleOption>::defaulted(#namestr, #desc)
                        },
                    }
                }
                None => quote! {
                    #label <#ty as #krate::option::Options>::new()
                },
            })
        }
    };

    let initializer = match data.fields {
        Fields::Named(fields) => {
            let initializers: Vec<_> = fields
                .named
                .iter()
                .enumerate()
                .map(process_field(false))
                .collect::<Result<_, Error>>()?;

            quote! {
                Self { #( #initializers, )* }
            }
        }
        Fields::Unnamed(fields) => {
            let initializers: Vec<_> = fields
                .unnamed
                .iter()
                .enumerate()
                .map(process_field(true))
                .collect::<Result<_, Error>>()?;

            quote! {
                Self ( #( #initializers, )* )
            }
        }
        Fields::Unit => quote!(Self),
    };

    Ok(quote! {
        unsafe impl #krate::option::Options for #ident {
            fn new() -> Self {
                #initializer
            }
        }
    })
}