1 | use std::borrow::Cow; |
2 | |
3 | use crate::{ |
4 | attributes::{self, get_pyo3_options, take_attributes, NameAttribute}, |
5 | deprecations::Deprecations, |
6 | }; |
7 | use proc_macro2::{Ident, TokenStream}; |
8 | use quote::quote; |
9 | use syn::{ |
10 | ext::IdentExt, |
11 | parse::{Parse, ParseStream}, |
12 | spanned::Spanned, |
13 | Result, |
14 | }; |
15 | |
16 | pub struct ConstSpec { |
17 | pub rust_ident: syn::Ident, |
18 | pub attributes: ConstAttributes, |
19 | } |
20 | |
21 | impl ConstSpec { |
22 | pub fn python_name(&self) -> Cow<'_, Ident> { |
23 | if let Some(name: &KeywordAttribute) = &self.attributes.name { |
24 | Cow::Borrowed(&name.value.0) |
25 | } else { |
26 | Cow::Owned(self.rust_ident.unraw()) |
27 | } |
28 | } |
29 | |
30 | /// Null-terminated Python name |
31 | pub fn null_terminated_python_name(&self) -> TokenStream { |
32 | let name: String = format!(" {}\0" , self.python_name()); |
33 | quote!({#name}) |
34 | } |
35 | } |
36 | |
37 | pub struct ConstAttributes { |
38 | pub is_class_attr: bool, |
39 | pub name: Option<NameAttribute>, |
40 | pub deprecations: Deprecations, |
41 | } |
42 | |
43 | pub enum PyO3ConstAttribute { |
44 | Name(NameAttribute), |
45 | } |
46 | |
47 | impl Parse for PyO3ConstAttribute { |
48 | fn parse(input: ParseStream<'_>) -> Result<Self> { |
49 | let lookahead: Lookahead1<'_> = input.lookahead1(); |
50 | if lookahead.peek(token:attributes::kw::name) { |
51 | input.parse().map(op:PyO3ConstAttribute::Name) |
52 | } else { |
53 | Err(lookahead.error()) |
54 | } |
55 | } |
56 | } |
57 | |
58 | impl ConstAttributes { |
59 | pub fn from_attrs(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Self> { |
60 | let mut attributes = ConstAttributes { |
61 | is_class_attr: false, |
62 | name: None, |
63 | deprecations: Deprecations::new(), |
64 | }; |
65 | |
66 | take_attributes(attrs, |attr| { |
67 | if attr.path().is_ident("classattr" ) { |
68 | ensure_spanned!( |
69 | matches!(attr.meta, syn::Meta::Path(..)), |
70 | attr.span() => "`#[classattr]` does not take any arguments" |
71 | ); |
72 | attributes.is_class_attr = true; |
73 | Ok(true) |
74 | } else if let Some(pyo3_attributes) = get_pyo3_options(attr)? { |
75 | for pyo3_attr in pyo3_attributes { |
76 | match pyo3_attr { |
77 | PyO3ConstAttribute::Name(name) => attributes.set_name(name)?, |
78 | } |
79 | } |
80 | Ok(true) |
81 | } else { |
82 | Ok(false) |
83 | } |
84 | })?; |
85 | |
86 | Ok(attributes) |
87 | } |
88 | |
89 | fn set_name(&mut self, name: NameAttribute) -> Result<()> { |
90 | ensure_spanned!( |
91 | self.name.is_none(), |
92 | name.span() => "`name` may only be specified once" |
93 | ); |
94 | self.name = Some(name); |
95 | Ok(()) |
96 | } |
97 | } |
98 | |