codegraph_python/entities/
function.rs

1use serde::{Deserialize, Serialize};
2
3/// Represents a function parameter
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
5pub struct Parameter {
6    /// Parameter name
7    pub name: String,
8
9    /// Type annotation (if present)
10    pub type_annotation: Option<String>,
11
12    /// Default value as string (if present)
13    pub default_value: Option<String>,
14}
15
16impl Parameter {
17    /// Create a new parameter with just a name
18    pub fn new(name: impl Into<String>) -> Self {
19        Self {
20            name: name.into(),
21            type_annotation: None,
22            default_value: None,
23        }
24    }
25
26    /// Create a parameter with type annotation
27    pub fn with_type(name: impl Into<String>, type_annotation: impl Into<String>) -> Self {
28        Self {
29            name: name.into(),
30            type_annotation: Some(type_annotation.into()),
31            default_value: None,
32        }
33    }
34
35    /// Add a default value
36    pub fn with_default(mut self, default: impl Into<String>) -> Self {
37        self.default_value = Some(default.into());
38        self
39    }
40}
41
42/// Represents a function or method in Python source code
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct FunctionEntity {
45    /// Function name
46    pub name: String,
47
48    /// Full signature (including parameters and return type)
49    pub signature: String,
50
51    /// Visibility: "public" (no underscore) or "private" (starts with _)
52    pub visibility: String,
53
54    /// Starting line number (1-indexed)
55    pub line_start: usize,
56
57    /// Ending line number (1-indexed)
58    pub line_end: usize,
59
60    /// Is this an async def function?
61    pub is_async: bool,
62
63    /// Is this a test function? (name starts with "test_" or "Test")
64    pub is_test: bool,
65
66    /// Function parameters
67    pub parameters: Vec<Parameter>,
68
69    /// Return type annotation (if present)
70    pub return_type: Option<String>,
71
72    /// Documentation string (docstring)
73    pub doc_comment: Option<String>,
74
75    /// Parent class name (for methods)
76    pub parent: Option<String>,
77
78    /// Decorators applied to the function (e.g., ["@staticmethod", "@cache"])
79    pub attributes: Vec<String>,
80}
81
82impl FunctionEntity {
83    /// Create a new function entity with required fields
84    pub fn new(name: impl Into<String>, line_start: usize, line_end: usize) -> Self {
85        let name = name.into();
86        let visibility = if name.starts_with('_') {
87            "private".to_string()
88        } else {
89            "public".to_string()
90        };
91
92        let is_test = name.starts_with("test_") || name.starts_with("Test");
93
94        Self {
95            name: name.clone(),
96            signature: format!("def {name}"),
97            visibility,
98            line_start,
99            line_end,
100            is_async: false,
101            is_test,
102            parameters: Vec::new(),
103            return_type: None,
104            doc_comment: None,
105            parent: None,
106            attributes: Vec::new(),
107        }
108    }
109
110    /// Check if this is a method (has parent class)
111    pub fn is_method(&self) -> bool {
112        self.parent.is_some()
113    }
114
115    /// Check if this is a static method
116    pub fn is_static(&self) -> bool {
117        self.attributes.iter().any(|a| a == "@staticmethod")
118    }
119
120    /// Check if this is a class method
121    pub fn is_classmethod(&self) -> bool {
122        self.attributes.iter().any(|a| a == "@classmethod")
123    }
124
125    /// Check if this is a property
126    pub fn is_property(&self) -> bool {
127        self.attributes.iter().any(|a| a == "@property")
128    }
129
130    /// Set the async flag
131    pub fn set_async(mut self, is_async: bool) -> Self {
132        self.is_async = is_async;
133        self
134    }
135
136    /// Set the return type
137    pub fn set_return_type(mut self, return_type: Option<String>) -> Self {
138        self.return_type = return_type;
139        self
140    }
141
142    /// Set the docstring
143    pub fn set_doc_comment(mut self, doc: Option<String>) -> Self {
144        self.doc_comment = doc;
145        self
146    }
147
148    /// Set the parent class (for methods)
149    pub fn set_parent(mut self, parent: Option<String>) -> Self {
150        self.parent = parent;
151        self
152    }
153
154    /// Add a decorator
155    pub fn add_attribute(mut self, attr: impl Into<String>) -> Self {
156        self.attributes.push(attr.into());
157        self
158    }
159
160    /// Add a parameter
161    pub fn add_parameter(mut self, param: Parameter) -> Self {
162        self.parameters.push(param);
163        self
164    }
165
166    /// Set the signature
167    pub fn set_signature(mut self, sig: impl Into<String>) -> Self {
168        self.signature = sig.into();
169        self
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[test]
178    fn test_parameter_new() {
179        let param = Parameter::new("arg");
180        assert_eq!(param.name, "arg");
181        assert!(param.type_annotation.is_none());
182        assert!(param.default_value.is_none());
183    }
184
185    #[test]
186    fn test_parameter_with_type() {
187        let param = Parameter::with_type("arg", "str");
188        assert_eq!(param.name, "arg");
189        assert_eq!(param.type_annotation, Some("str".to_string()));
190    }
191
192    #[test]
193    fn test_parameter_with_default() {
194        let param = Parameter::with_type("arg", "int").with_default("42");
195        assert_eq!(param.default_value, Some("42".to_string()));
196    }
197
198    #[test]
199    fn test_function_entity_visibility() {
200        let public_func = FunctionEntity::new("my_func", 1, 10);
201        assert_eq!(public_func.visibility, "public");
202
203        let private_func = FunctionEntity::new("_my_func", 1, 10);
204        assert_eq!(private_func.visibility, "private");
205    }
206
207    #[test]
208    fn test_function_entity_test_detection() {
209        let test_func = FunctionEntity::new("test_something", 1, 10);
210        assert!(test_func.is_test);
211
212        let regular_func = FunctionEntity::new("do_something", 1, 10);
213        assert!(!regular_func.is_test);
214    }
215
216    #[test]
217    fn test_function_entity_method_detection() {
218        let mut func = FunctionEntity::new("method", 1, 10);
219        assert!(!func.is_method());
220
221        func = func.set_parent(Some("MyClass".to_string()));
222        assert!(func.is_method());
223    }
224
225    #[test]
226    fn test_function_entity_decorators() {
227        let func = FunctionEntity::new("method", 1, 10).add_attribute("@staticmethod");
228
229        assert!(func.is_static());
230        assert!(!func.is_classmethod());
231        assert!(!func.is_property());
232    }
233}