codegraph_python/entities/
class.rs1use super::function::FunctionEntity;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6pub struct Field {
7 pub name: String,
9
10 pub type_annotation: Option<String>,
12
13 pub visibility: String,
15
16 pub is_static: bool,
18}
19
20impl Field {
21 pub fn new(name: impl Into<String>) -> Self {
23 let name = name.into();
24 let visibility = if name.starts_with("__") && name.ends_with("__") {
25 "public".to_string() } else if name.starts_with("__") {
27 "private".to_string()
28 } else if name.starts_with('_') {
29 "protected".to_string()
30 } else {
31 "public".to_string()
32 };
33
34 Self {
35 name,
36 type_annotation: None,
37 visibility,
38 is_static: false,
39 }
40 }
41
42 pub fn set_type_annotation(mut self, type_ann: Option<String>) -> Self {
44 self.type_annotation = type_ann;
45 self
46 }
47
48 pub fn set_static(mut self, is_static: bool) -> Self {
50 self.is_static = is_static;
51 self
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
57pub struct ClassEntity {
58 pub name: String,
60
61 pub visibility: String,
63
64 pub line_start: usize,
66
67 pub line_end: usize,
69
70 pub is_abstract: bool,
72
73 pub base_classes: Vec<String>,
75
76 pub implemented_traits: Vec<String>,
78
79 pub methods: Vec<FunctionEntity>,
81
82 pub fields: Vec<Field>,
84
85 pub doc_comment: Option<String>,
87
88 pub attributes: Vec<String>,
90}
91
92impl ClassEntity {
93 pub fn new(name: impl Into<String>, line_start: usize, line_end: usize) -> Self {
95 let name = name.into();
96 let visibility = if name.starts_with('_') {
97 "private".to_string()
98 } else {
99 "public".to_string()
100 };
101
102 Self {
103 name,
104 visibility,
105 line_start,
106 line_end,
107 is_abstract: false,
108 base_classes: Vec::new(),
109 implemented_traits: Vec::new(),
110 methods: Vec::new(),
111 fields: Vec::new(),
112 doc_comment: None,
113 attributes: Vec::new(),
114 }
115 }
116
117 pub fn is_dataclass(&self) -> bool {
119 self.attributes.iter().any(|a| a == "@dataclass")
120 }
121
122 pub fn is_abstract(&self) -> bool {
124 self.is_abstract
125 || self
126 .base_classes
127 .iter()
128 .any(|b| b == "ABC" || b == "ABCMeta")
129 || self.attributes.iter().any(|a| a.contains("abstractmethod"))
130 }
131
132 pub fn has_method(&self, method_name: &str) -> bool {
134 self.methods.iter().any(|m| m.name == method_name)
135 }
136
137 pub fn set_abstract(mut self, is_abstract: bool) -> Self {
139 self.is_abstract = is_abstract;
140 self
141 }
142
143 pub fn add_base_class(mut self, base: impl Into<String>) -> Self {
145 self.base_classes.push(base.into());
146 self
147 }
148
149 pub fn add_trait(mut self, trait_name: impl Into<String>) -> Self {
151 self.implemented_traits.push(trait_name.into());
152 self
153 }
154
155 pub fn add_method(mut self, method: FunctionEntity) -> Self {
157 self.methods.push(method);
158 self
159 }
160
161 pub fn add_field(mut self, field: Field) -> Self {
163 self.fields.push(field);
164 self
165 }
166
167 pub fn set_doc_comment(mut self, doc: Option<String>) -> Self {
169 self.doc_comment = doc;
170 self
171 }
172
173 pub fn add_attribute(mut self, attr: impl Into<String>) -> Self {
175 self.attributes.push(attr.into());
176 self
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_field_visibility() {
186 let public_field = Field::new("name");
187 assert_eq!(public_field.visibility, "public");
188
189 let protected_field = Field::new("_name");
190 assert_eq!(protected_field.visibility, "protected");
191
192 let private_field = Field::new("__name");
193 assert_eq!(private_field.visibility, "private");
194
195 let dunder_field = Field::new("__init__");
196 assert_eq!(dunder_field.visibility, "public"); }
198
199 #[test]
200 fn test_class_entity_visibility() {
201 let public_class = ClassEntity::new("MyClass", 1, 10);
202 assert_eq!(public_class.visibility, "public");
203
204 let private_class = ClassEntity::new("_MyClass", 1, 10);
205 assert_eq!(private_class.visibility, "private");
206 }
207
208 #[test]
209 fn test_class_entity_dataclass() {
210 let class = ClassEntity::new("User", 1, 10).add_attribute("@dataclass");
211
212 assert!(class.is_dataclass());
213 }
214
215 #[test]
216 fn test_class_entity_abstract() {
217 let class1 = ClassEntity::new("Base", 1, 10).add_base_class("ABC");
218 assert!(class1.is_abstract());
219
220 let class2 = ClassEntity::new("Base", 1, 10).set_abstract(true);
221 assert!(class2.is_abstract());
222 }
223
224 #[test]
225 fn test_class_entity_methods() {
226 let method1 = FunctionEntity::new("method1", 2, 4);
227 let method2 = FunctionEntity::new("method2", 5, 7);
228
229 let class = ClassEntity::new("MyClass", 1, 10)
230 .add_method(method1)
231 .add_method(method2);
232
233 assert!(class.has_method("method1"));
234 assert!(class.has_method("method2"));
235 assert!(!class.has_method("method3"));
236 assert_eq!(class.methods.len(), 2);
237 }
238}