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<&'static str>,
15    pub deprecated: Option<DeprecatedInfo>,
16}
17
18impl Import for MemberDef {
19    fn import(&self) -> HashSet<ModuleRef> {
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(|s| s.as_str()),
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        // Add deprecated decorator if present
104        if let Some(deprecated) = &self.0.deprecated {
105            writeln!(f, "{indent}{deprecated}")?;
106        }
107        write!(
108            f,
109            "{indent}@{}.setter\n{indent}def {}(self, value: {}) -> None:",
110            self.0.name, self.0.name, self.0.r#type
111        )?;
112        let doc = if let Some(default) = self.0.default {
113            if default == "..." {
114                Cow::Borrowed(self.0.doc)
115            } else {
116                Cow::Owned(format!(
117                    "{}\n```python\ndefault = {default}\n```",
118                    self.0.doc
119                ))
120            }
121        } else {
122            Cow::Borrowed(self.0.doc)
123        };
124        if !doc.is_empty() {
125            writeln!(f)?;
126            let double_indent = format!("{indent}{indent}");
127            docstring::write_docstring(f, &doc, &double_indent)
128        } else {
129            writeln!(f, " ...")
130        }
131    }
132}