pyo3_macros_backend/
derive_attributes.rs1use crate::attributes::{
2 self, get_pyo3_options, CrateAttribute, DefaultAttribute, FromPyWithAttribute,
3 IntoPyWithAttribute, RenameAllAttribute,
4};
5use proc_macro2::Span;
6use syn::parse::{Parse, ParseStream};
7use syn::spanned::Spanned;
8use syn::{parenthesized, Attribute, LitStr, Result, Token};
9
10pub enum ContainerAttribute {
12 Transparent(attributes::kw::transparent),
14 ItemAll(attributes::kw::from_item_all),
16 ErrorAnnotation(LitStr),
18 Crate(CrateAttribute),
20 RenameAll(RenameAllAttribute),
22}
23
24impl Parse for ContainerAttribute {
25 fn parse(input: ParseStream<'_>) -> Result<Self> {
26 let lookahead = input.lookahead1();
27 if lookahead.peek(attributes::kw::transparent) {
28 let kw: attributes::kw::transparent = input.parse()?;
29 Ok(ContainerAttribute::Transparent(kw))
30 } else if lookahead.peek(attributes::kw::from_item_all) {
31 let kw: attributes::kw::from_item_all = input.parse()?;
32 Ok(ContainerAttribute::ItemAll(kw))
33 } else if lookahead.peek(attributes::kw::annotation) {
34 let _: attributes::kw::annotation = input.parse()?;
35 let _: Token![=] = input.parse()?;
36 input.parse().map(ContainerAttribute::ErrorAnnotation)
37 } else if lookahead.peek(Token![crate]) {
38 input.parse().map(ContainerAttribute::Crate)
39 } else if lookahead.peek(attributes::kw::rename_all) {
40 input.parse().map(ContainerAttribute::RenameAll)
41 } else {
42 Err(lookahead.error())
43 }
44 }
45}
46
47#[derive(Default)]
48pub struct ContainerAttributes {
49 pub transparent: Option<attributes::kw::transparent>,
51 pub from_item_all: Option<attributes::kw::from_item_all>,
53 pub annotation: Option<syn::LitStr>,
55 pub krate: Option<CrateAttribute>,
57 pub rename_all: Option<RenameAllAttribute>,
59}
60
61impl ContainerAttributes {
62 pub fn from_attrs(attrs: &[Attribute]) -> Result<Self> {
63 let mut options = ContainerAttributes::default();
64
65 for attr in attrs {
66 if let Some(pyo3_attrs) = get_pyo3_options(attr)? {
67 pyo3_attrs
68 .into_iter()
69 .try_for_each(|opt| options.set_option(opt))?;
70 }
71 }
72 Ok(options)
73 }
74
75 fn set_option(&mut self, option: ContainerAttribute) -> syn::Result<()> {
76 macro_rules! set_option {
77 ($key:ident) => {
78 {
79 ensure_spanned!(
80 self.$key.is_none(),
81 $key.span() => concat!("`", stringify!($key), "` may only be specified once")
82 );
83 self.$key = Some($key);
84 }
85 };
86 }
87
88 match option {
89 ContainerAttribute::Transparent(transparent) => set_option!(transparent),
90 ContainerAttribute::ItemAll(from_item_all) => set_option!(from_item_all),
91 ContainerAttribute::ErrorAnnotation(annotation) => set_option!(annotation),
92 ContainerAttribute::Crate(krate) => set_option!(krate),
93 ContainerAttribute::RenameAll(rename_all) => set_option!(rename_all),
94 }
95 Ok(())
96 }
97}
98
99#[derive(Clone, Debug)]
100pub enum FieldGetter {
101 GetItem(attributes::kw::item, Option<syn::Lit>),
102 GetAttr(attributes::kw::attribute, Option<syn::LitStr>),
103}
104
105impl FieldGetter {
106 pub fn span(&self) -> Span {
107 match self {
108 FieldGetter::GetItem(item, _) => item.span,
109 FieldGetter::GetAttr(attribute, _) => attribute.span,
110 }
111 }
112}
113
114pub enum FieldAttribute {
115 Getter(FieldGetter),
116 FromPyWith(FromPyWithAttribute),
117 IntoPyWith(IntoPyWithAttribute),
118 Default(DefaultAttribute),
119}
120
121impl Parse for FieldAttribute {
122 fn parse(input: ParseStream<'_>) -> Result<Self> {
123 let lookahead = input.lookahead1();
124 if lookahead.peek(attributes::kw::attribute) {
125 let attr_kw: attributes::kw::attribute = input.parse()?;
126 if input.peek(syn::token::Paren) {
127 let content;
128 let _ = parenthesized!(content in input);
129 let attr_name: LitStr = content.parse()?;
130 if !content.is_empty() {
131 return Err(content.error(
132 "expected at most one argument: `attribute` or `attribute(\"name\")`",
133 ));
134 }
135 ensure_spanned!(
136 !attr_name.value().is_empty(),
137 attr_name.span() => "attribute name cannot be empty"
138 );
139 Ok(Self::Getter(FieldGetter::GetAttr(attr_kw, Some(attr_name))))
140 } else {
141 Ok(Self::Getter(FieldGetter::GetAttr(attr_kw, None)))
142 }
143 } else if lookahead.peek(attributes::kw::item) {
144 let item_kw: attributes::kw::item = input.parse()?;
145 if input.peek(syn::token::Paren) {
146 let content;
147 let _ = parenthesized!(content in input);
148 let key = content.parse()?;
149 if !content.is_empty() {
150 return Err(
151 content.error("expected at most one argument: `item` or `item(key)`")
152 );
153 }
154 Ok(Self::Getter(FieldGetter::GetItem(item_kw, Some(key))))
155 } else {
156 Ok(Self::Getter(FieldGetter::GetItem(item_kw, None)))
157 }
158 } else if lookahead.peek(attributes::kw::from_py_with) {
159 input.parse().map(Self::FromPyWith)
160 } else if lookahead.peek(attributes::kw::into_py_with) {
161 input.parse().map(FieldAttribute::IntoPyWith)
162 } else if lookahead.peek(Token![default]) {
163 input.parse().map(Self::Default)
164 } else {
165 Err(lookahead.error())
166 }
167 }
168}
169
170#[derive(Clone, Debug, Default)]
171pub struct FieldAttributes {
172 pub getter: Option<FieldGetter>,
173 pub from_py_with: Option<FromPyWithAttribute>,
174 pub into_py_with: Option<IntoPyWithAttribute>,
175 pub default: Option<DefaultAttribute>,
176}
177
178impl FieldAttributes {
179 pub fn from_attrs(attrs: &[Attribute]) -> Result<Self> {
181 let mut options = FieldAttributes::default();
182
183 for attr in attrs {
184 if let Some(pyo3_attrs) = get_pyo3_options(attr)? {
185 pyo3_attrs
186 .into_iter()
187 .try_for_each(|opt| options.set_option(opt))?;
188 }
189 }
190 Ok(options)
191 }
192
193 fn set_option(&mut self, option: FieldAttribute) -> syn::Result<()> {
194 macro_rules! set_option {
195 ($key:ident) => {
196 set_option!($key, concat!("`", stringify!($key), "` may only be specified once"))
197 };
198 ($key:ident, $msg: expr) => {{
199 ensure_spanned!(
200 self.$key.is_none(),
201 $key.span() => $msg
202 );
203 self.$key = Some($key);
204 }}
205 }
206
207 match option {
208 FieldAttribute::Getter(getter) => {
209 set_option!(getter, "only one of `attribute` or `item` can be provided")
210 }
211 FieldAttribute::FromPyWith(from_py_with) => set_option!(from_py_with),
212 FieldAttribute::IntoPyWith(into_py_with) => set_option!(into_py_with),
213 FieldAttribute::Default(default) => set_option!(default),
214 }
215 Ok(())
216 }
217}