pyo3_stub_gen/generate/
member.rs

1use crate::{generate::*, type_info::*, TypeInfo};
2use std::{
3    borrow::Cow,
4    collections::HashSet,
5    fmt::{self},
6};
7
8/// Definition of a class member.
9#[derive(Debug, Clone, PartialEq)]
10pub struct MemberDef {
11    pub name: &'static str,
12    pub r#type: TypeInfo,
13    pub doc: &'static str,
14    pub default: Option<String>,
15    pub deprecated: Option<DeprecatedInfo>,
16}
17
18impl Import for MemberDef {
19    fn import(&self) -> HashSet<ImportRef> {
20        let mut import = self.r#type.import.clone();
21        // Add typing_extensions import if deprecated
22        if self.deprecated.is_some() {
23            import.insert("typing_extensions".into());
24        }
25        import
26    }
27}
28
29impl From<&MemberInfo> for MemberDef {
30    fn from(info: &MemberInfo) -> Self {
31        Self {
32            name: info.name,
33            r#type: (info.r#type)(),
34            doc: info.doc,
35            default: info.default.map(|f| f()),
36            deprecated: info.deprecated.clone(),
37        }
38    }
39}
40
41impl fmt::Display for MemberDef {
42    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43        let indent = indent();
44        // Constants cannot have deprecated decorators in Python syntax
45        // Log a warning if deprecated is present but will be ignored
46        if let Some(_deprecated) = &self.deprecated {
47            log::warn!(
48                "Ignoring #[deprecated] on constant '{}': Python constants cannot have decorators. \
49                Consider using a function instead if deprecation is needed.",
50                self.name
51            );
52        }
53        write!(f, "{indent}{}: {}", self.name, self.r#type)?;
54        if let Some(default) = &self.default {
55            write!(f, " = {default}")?;
56        }
57        writeln!(f)?;
58        docstring::write_docstring(f, self.doc, indent)?;
59        Ok(())
60    }
61}
62
63pub struct GetterDisplay<'a>(pub &'a MemberDef);
64pub struct SetterDisplay<'a>(pub &'a MemberDef);
65
66impl fmt::Display for GetterDisplay<'_> {
67    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        let indent = indent();
69        // Add deprecated decorator if present
70        if let Some(deprecated) = &self.0.deprecated {
71            writeln!(f, "{indent}{deprecated}")?;
72        }
73        write!(
74            f,
75            "{indent}@property\n{indent}def {}(self) -> {}:",
76            self.0.name, self.0.r#type
77        )?;
78        let doc = if let Some(default) = &self.0.default {
79            if default == "..." {
80                Cow::Borrowed(self.0.doc)
81            } else {
82                Cow::Owned(format!(
83                    "{}\n```python\ndefault = {default}\n```",
84                    self.0.doc
85                ))
86            }
87        } else {
88            Cow::Borrowed(self.0.doc)
89        };
90        if !doc.is_empty() {
91            writeln!(f)?;
92            let double_indent = format!("{indent}{indent}");
93            docstring::write_docstring(f, &doc, &double_indent)
94        } else {
95            writeln!(f, " ...")
96        }
97    }
98}
99
100impl fmt::Display for SetterDisplay<'_> {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        let indent = indent();
103        // Write setter decorator first, then deprecated decorator
104        writeln!(f, "{indent}@{}.setter", self.0.name)?;
105        if let Some(deprecated) = &self.0.deprecated {
106            writeln!(f, "{indent}{deprecated}")?;
107        }
108        write!(
109            f,
110            "{indent}def {}(self, value: {}) -> None:",
111            self.0.name, self.0.r#type
112        )?;
113        let doc = if let Some(default) = &self.0.default {
114            if default == "..." {
115                Cow::Borrowed(self.0.doc)
116            } else {
117                Cow::Owned(format!(
118                    "{}\n```python\ndefault = {default}\n```",
119                    self.0.doc
120                ))
121            }
122        } else {
123            Cow::Borrowed(self.0.doc)
124        };
125        if !doc.is_empty() {
126            writeln!(f)?;
127            let double_indent = format!("{indent}{indent}");
128            docstring::write_docstring(f, &doc, &double_indent)
129        } else {
130            writeln!(f, " ...")
131        }
132    }
133}