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
63impl MemberDef {
64    /// Format member with module-qualified type names
65    ///
66    /// This method uses the target module context to qualify type identifiers
67    /// within compound type expressions based on their source modules.
68    pub fn fmt_for_module(
69        &self,
70        target_module: &str,
71        f: &mut fmt::Formatter,
72        indent: &str,
73    ) -> fmt::Result {
74        // Constants cannot have deprecated decorators in Python syntax
75        // Log a warning if deprecated is present but will be ignored
76        if let Some(_deprecated) = &self.deprecated {
77            log::warn!(
78                "Ignoring #[deprecated] on constant '{}': Python constants cannot have decorators. \
79                Consider using a function instead if deprecation is needed.",
80                self.name
81            );
82        }
83        let qualified_type = self.r#type.qualified_for_module(target_module);
84        write!(f, "{indent}{}: {}", self.name, qualified_type)?;
85        if let Some(default) = &self.default {
86            write!(f, " = {default}")?;
87        }
88        writeln!(f)?;
89        docstring::write_docstring(f, self.doc, indent)?;
90        Ok(())
91    }
92}
93
94pub struct GetterDisplay<'a> {
95    pub member: &'a MemberDef,
96    pub target_module: &'a str,
97}
98
99pub struct SetterDisplay<'a> {
100    pub member: &'a MemberDef,
101    pub target_module: &'a str,
102}
103
104impl fmt::Display for GetterDisplay<'_> {
105    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106        let indent = indent();
107        let qualified_type = self.member.r#type.qualified_for_module(self.target_module);
108        // Add deprecated decorator if present
109        if let Some(deprecated) = &self.member.deprecated {
110            writeln!(f, "{indent}{deprecated}")?;
111        }
112        write!(
113            f,
114            "{indent}@property\n{indent}def {}(self) -> {}:",
115            self.member.name, qualified_type
116        )?;
117        let doc = if let Some(default) = &self.member.default {
118            if default == "..." {
119                Cow::Borrowed(self.member.doc)
120            } else {
121                Cow::Owned(format!(
122                    "{}\n```python\ndefault = {default}\n```",
123                    self.member.doc
124                ))
125            }
126        } else {
127            Cow::Borrowed(self.member.doc)
128        };
129        if !doc.is_empty() {
130            writeln!(f)?;
131            let double_indent = format!("{indent}{indent}");
132            docstring::write_docstring(f, &doc, &double_indent)
133        } else {
134            writeln!(f, " ...")
135        }
136    }
137}
138
139impl fmt::Display for SetterDisplay<'_> {
140    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141        let indent = indent();
142        let qualified_type = self.member.r#type.qualified_for_module(self.target_module);
143        // Write setter decorator first, then deprecated decorator
144        writeln!(f, "{indent}@{}.setter", self.member.name)?;
145        if let Some(deprecated) = &self.member.deprecated {
146            writeln!(f, "{indent}{deprecated}")?;
147        }
148        write!(
149            f,
150            "{indent}def {}(self, value: {}) -> None:",
151            self.member.name, qualified_type
152        )?;
153        let doc = if let Some(default) = &self.member.default {
154            if default == "..." {
155                Cow::Borrowed(self.member.doc)
156            } else {
157                Cow::Owned(format!(
158                    "{}\n```python\ndefault = {default}\n```",
159                    self.member.doc
160                ))
161            }
162        } else {
163            Cow::Borrowed(self.member.doc)
164        };
165        if !doc.is_empty() {
166            writeln!(f)?;
167            let double_indent = format!("{indent}{indent}");
168            docstring::write_docstring(f, &doc, &double_indent)
169        } else {
170            writeln!(f, " ...")
171        }
172    }
173}