diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index cd149cddd6..d8218b606a 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -1,7 +1,7 @@ use super::Diagnostic; use crate::util::{ path_eq, pyclass_ident_and_attrs, ClassItemMeta, ContentItem, ContentItemInner, ErrorVec, - ItemMeta, ItemMetaInner, ItemNursery, ALL_ALLOWED_NAMES, + ItemMeta, ItemMetaInner, ItemNursery, SimpleItemMeta, ALL_ALLOWED_NAMES, }; use proc_macro2::TokenStream; use quote::{quote, quote_spanned, ToTokens}; @@ -216,6 +216,11 @@ struct SlotItem { inner: ContentItemInner, } +/// #[pyattr] +struct AttributeItem { + inner: ContentItemInner, +} + /// #[extend_class] struct ExtendClassItem { inner: ContentItemInner, @@ -236,6 +241,11 @@ impl ContentItem for SlotItem { &self.inner } } +impl ContentItem for AttributeItem { + fn inner(&self) -> &ContentItemInner { + &self.inner + } +} impl ContentItem for ExtendClassItem { fn inner(&self) -> &ContentItemInner { &self.inner @@ -357,6 +367,51 @@ where Ok(()) } } + +impl ImplItem for AttributeItem +where + Item: ItemLike + ToTokens + GetIdent, +{ + fn gen_impl_item(&self, args: ImplItemArgs<'_, Item>) -> Result<()> { + let cfgs = args.cfgs.to_vec(); + let attr = args.attrs.remove(self.index()); + + let get_py_name = |attr: &Attribute, ident: &Ident| -> Result<_> { + let item_meta = SimpleItemMeta::from_attr(ident.clone(), attr)?; + let py_name = item_meta.simple_name()?; + Ok(py_name) + }; + let (py_name, tokens) = if args.item.is_function_or_method() || args.item.is_const() { + let ident = args.item.get_ident().unwrap(); + let py_name = get_py_name(&attr, &ident)?; + + let value = if args.item.is_const() { + // TODO: ctx.new_value + quote_spanned!(ident.span() => ctx.new_int(Self::#ident)) + } else { + quote_spanned!(ident.span() => Self::#ident(ctx)) + }; + ( + py_name.clone(), + quote! { + class.set_str_attr(#py_name, #value); + }, + ) + } else { + return Err(self.new_syn_error( + args.item.span(), + "can only be on a const or an associated method without argument", + )); + }; + + args.context + .impl_extend_items + .add_item(py_name, cfgs, tokens)?; + + Ok(()) + } +} + impl ImplItem for ExtendClassItem where Item: ItemLike + ToTokens + GetIdent, @@ -705,6 +760,9 @@ where "pyslot" => Box::new(SlotItem { inner: ContentItemInner { index, attr_name }, }), + "pyattr" => Box::new(AttributeItem { + inner: ContentItemInner { index, attr_name }, + }), "extend_class" => Box::new(ExtendClassItem { inner: ContentItemInner { index, attr_name }, }),