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