fn expand_attrs_derive()

in starlark_derive/src/attrs.rs [90:182]


fn expand_attrs_derive(data: Data, name: Ident) -> Result<proc_macro2::TokenStream> {
    let fields: Vec<_> = match data {
        Data::Struct(s) => Ok(s.fields.iter().cloned().collect()),
        Data::Enum(e) => Err(Error::new(
            e.enum_token.span(),
            "#[derive(StarlarkAttrs)] does not support enums",
        )),
        Data::Union(u) => Err(Error::new(
            u.union_token.span(),
            "#[derive(StarlarkAttrs)] does not support unions",
        )),
    }?;

    let expose_fields: Vec<Field> = fields
        .into_iter()
        .map(|field| {
            match field_attr(&field, "starlark") {
                // by default, include all fields
                None => Ok(Field {
                    ident: field.ident.unwrap(),
                    starlark_args: vec![],
                    ty: field.ty,
                }),
                Some(attr) => match attr.parse_meta() {
                    Ok(Meta::List(lst)) => {
                        let starlark_args = lst
                            .nested
                            .iter()
                            .map(|m| match m {
                                NestedMeta::Meta(Meta::Path(p)) => p
                                    .get_ident()
                                    .ok_or_else(|| Error::new(m.span(), STARLARK_ATTR_ERR_MSG))
                                    .map(|i| i.clone()),
                                _ => Err(Error::new(m.span(), STARLARK_ATTR_ERR_MSG)),
                            })
                            .collect::<Result<_>>()?;
                        Ok(Field {
                            ident: field.ident.unwrap(),
                            starlark_args,
                            ty: field.ty,
                        })
                    }
                    Ok(_) => Err(Error::new(attr.span(), "starlark attr must parse as list")),
                    Err(e) => Err(e),
                },
            }
        })
        .filter(|f| f.as_ref().map(|f| !f.skip()).unwrap_or(true))
        .collect::<Result<_>>()?;

    let has_attr_items = expose_fields.iter().map(|f| f.has_attr_match_item());
    let has_attr = quote! {
        pub(crate) fn attrs_has_attr(&self, attr: &str) -> bool {
            match attr {
                #(#has_attr_items),*,
                _ => false,
            }
        }
    };

    let get_attr_items = expose_fields.iter().map(|f| f.get_attr_match_item());
    let get_attr = quote! {
        pub(crate) fn attrs_get_attr<'v>(&self, attr: &str, heap: &'v starlark::values::Heap) -> Option<starlark::values::Value<'v>> {
            match attr {
                #(#get_attr_items),*,
                _ => None,
            }
        }
    };

    let dir_names = expose_fields.iter().map(|f| f.ident.to_string());
    let dir_attr = quote! {
        pub(crate) fn attrs_dir_attr(&self) -> Vec<String> {
            vec![
                #(#dir_names.to_owned()),*
            ]
        }
    };

    let expanded = quote! {
        // Unfortunately, we can't actually implement the direct methods for
        // `StarlarkValue`, because then we would have conflicting
        // implementations. However, we can implement wrappers in another
        // proc-macro that can then be called.
        impl #name {
            #has_attr
            #get_attr
            #dir_attr
        }
    };

    Ok(expanded)
}