Skip to content

Commit 2c29a33

Browse files
committed
Add support for deleter on getsets
1 parent fbc8ebc commit 2c29a33

3 files changed

Lines changed: 223 additions & 82 deletions

File tree

derive/src/pyclass.rs

Lines changed: 87 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -354,11 +354,10 @@ where
354354
let item_attr = args.attrs.remove(self.index());
355355
let item_meta = PropertyItemMeta::from_attr(ident.clone(), &item_attr)?;
356356

357-
let py_name = item_meta.property_name()?;
358-
let setter = item_meta.setter()?;
357+
let (py_name, kind) = item_meta.property_name()?;
359358
args.context
360359
.getset_items
361-
.add_item(py_name, args.cfgs.to_vec(), setter, ident.clone())?;
360+
.add_item(py_name, args.cfgs.to_vec(), kind, ident.clone())?;
362361
Ok(())
363362
}
364363
}
@@ -478,27 +477,37 @@ where
478477
#[derive(Default)]
479478
#[allow(clippy::type_complexity)]
480479
struct GetSetNursery {
481-
map: HashMap<(String, Vec<Attribute>), (Option<Ident>, Option<Ident>)>,
480+
map: HashMap<(String, Vec<Attribute>), (Option<Ident>, Option<Ident>, Option<Ident>)>,
482481
validated: bool,
483482
}
484483

484+
enum GetSetItemKind {
485+
Get,
486+
Set,
487+
Delete,
488+
}
489+
485490
impl GetSetNursery {
486491
fn add_item(
487492
&mut self,
488493
name: String,
489494
cfgs: Vec<Attribute>,
490-
setter: bool,
495+
kind: GetSetItemKind,
491496
item_ident: Ident,
492497
) -> Result<()> {
493498
assert!(!self.validated, "new item is not allowed after validation");
494-
if setter && !cfgs.is_empty() {
499+
if !matches!(kind, GetSetItemKind::Get) && !cfgs.is_empty() {
495500
return Err(syn::Error::new_spanned(
496501
item_ident,
497-
"Property setter does not allow #[cfg]",
502+
"Only the getter can have #[cfg]",
498503
));
499504
}
500505
let entry = self.map.entry((name.clone(), cfgs)).or_default();
501-
let func = if setter { &mut entry.1 } else { &mut entry.0 };
506+
let func = match kind {
507+
GetSetItemKind::Get => &mut entry.0,
508+
GetSetItemKind::Set => &mut entry.1,
509+
GetSetItemKind::Delete => &mut entry.2,
510+
};
502511
if func.is_some() {
503512
return Err(syn::Error::new_spanned(
504513
item_ident,
@@ -511,10 +520,10 @@ impl GetSetNursery {
511520

512521
fn validate(&mut self) -> Result<()> {
513522
let mut errors = Vec::new();
514-
for ((name, _cfgs), (getter, setter)) in self.map.iter() {
523+
for ((name, _cfgs), (getter, setter, deleter)) in self.map.iter() {
515524
if getter.is_none() {
516525
errors.push(syn::Error::new_spanned(
517-
setter.as_ref().unwrap(),
526+
setter.as_ref().or_else(|| deleter.as_ref()).unwrap(),
518527
format!("Property '{}' is missing a getter", name),
519528
));
520529
};
@@ -528,21 +537,33 @@ impl GetSetNursery {
528537
impl ToTokens for GetSetNursery {
529538
fn to_tokens(&self, tokens: &mut TokenStream) {
530539
assert!(self.validated, "Call `validate()` before token generation");
531-
tokens.extend(self.map.iter().map(|((name, cfgs), (getter, setter))| {
532-
let (constructor, setter) = match setter {
533-
Some(setter) => (quote! { with_get_set }, quote_spanned! { setter.span() => , &Self::#setter }),
534-
None => (quote! { with_get }, quote! { }),
535-
};
536-
quote! {
537-
#( #cfgs )*
538-
class.set_str_attr(
539-
#name,
540-
::rustpython_vm::pyobject::PyObject::new(
541-
::rustpython_vm::builtins::PyGetSet::#constructor(#name.into(), &Self::#getter #setter),
542-
ctx.types.getset_type.clone(), None)
543-
);
544-
}
545-
}));
540+
let properties = self
541+
.map
542+
.iter()
543+
.map(|((name, cfgs), (getter, setter, deleter))| {
544+
let setter = match setter {
545+
Some(setter) => quote_spanned! { setter.span()=> .with_set(&Self::#setter)},
546+
None => quote! {},
547+
};
548+
let deleter = match deleter {
549+
Some(deleter) => {
550+
quote_spanned! { deleter.span()=> .with_delete(&Self::#deleter)}
551+
}
552+
None => quote! {},
553+
};
554+
quote! {
555+
#( #cfgs )*
556+
class.set_str_attr(
557+
#name,
558+
::rustpython_vm::pyobject::PyObject::new(
559+
::rustpython_vm::builtins::PyGetSet::new(#name.into())
560+
.with_get(&Self::#getter)
561+
#setter #deleter,
562+
ctx.types.getset_type.clone(), None)
563+
);
564+
}
565+
});
566+
tokens.extend(properties);
546567
}
547568
}
548569

@@ -580,7 +601,7 @@ impl MethodItemMeta {
580601
struct PropertyItemMeta(ItemMetaInner);
581602

582603
impl ItemMeta for PropertyItemMeta {
583-
const ALLOWED_NAMES: &'static [&'static str] = &["name", "magic", "setter"];
604+
const ALLOWED_NAMES: &'static [&'static str] = &["name", "magic", "setter", "deleter"];
584605

585606
fn from_inner(inner: ItemMetaInner) -> Self {
586607
Self(inner)
@@ -591,10 +612,25 @@ impl ItemMeta for PropertyItemMeta {
591612
}
592613

593614
impl PropertyItemMeta {
594-
fn property_name(&self) -> Result<String> {
615+
fn property_name(&self) -> Result<(String, GetSetItemKind)> {
595616
let inner = self.inner();
596617
let magic = inner._bool("magic")?;
597618
let setter = inner._bool("setter")?;
619+
let deleter = inner._bool("deleter")?;
620+
let kind = match (setter, deleter) {
621+
(false, false) => GetSetItemKind::Get,
622+
(true, false) => GetSetItemKind::Set,
623+
(false, true) => GetSetItemKind::Delete,
624+
(true, true) => {
625+
return Err(syn::Error::new_spanned(
626+
&inner.meta_ident,
627+
format!(
628+
"can't have both setter and deleter on a #[{}] fn",
629+
inner.meta_name()
630+
),
631+
))
632+
}
633+
};
598634
let name = inner._optional_str("name")?;
599635
let py_name = if let Some(name) = name {
600636
name
@@ -623,6 +659,29 @@ impl PropertyItemMeta {
623659
),
624660
));
625661
}
662+
} else if deleter {
663+
if let Some(name) = sig_name.strip_prefix("del_") {
664+
if name.is_empty() {
665+
return Err(syn::Error::new_spanned(
666+
&inner.meta_ident,
667+
format!(
668+
"A #[{}(deleter)] fn with a del_* name must \
669+
have something after \"del_\"",
670+
inner.meta_name()
671+
),
672+
));
673+
}
674+
name.to_string()
675+
} else {
676+
return Err(syn::Error::new_spanned(
677+
&inner.meta_ident,
678+
format!(
679+
"A #[{}(deleter)] fn must either have a `name` \
680+
parameter or a fn name along the lines of \"del_*\"",
681+
inner.meta_name()
682+
),
683+
));
684+
}
626685
} else {
627686
sig_name
628687
};
@@ -632,11 +691,7 @@ impl PropertyItemMeta {
632691
name
633692
}
634693
};
635-
Ok(py_name)
636-
}
637-
638-
fn setter(&self) -> Result<bool> {
639-
self.inner()._bool("setter")
694+
Ok((py_name, kind))
640695
}
641696
}
642697

0 commit comments

Comments
 (0)