codebank/parser/formatter/
python.rs1use crate::{BankStrategy, FileUnit, FunctionUnit, ModuleUnit, Result, StructUnit};
2
3pub trait PythonFormatter {
4 fn format_python(&self, strategy: BankStrategy) -> Result<String>;
5}
6
7impl PythonFormatter for FunctionUnit {
8 fn format_python(&self, strategy: BankStrategy) -> Result<String> {
9 let mut output = String::new();
10
11 match strategy {
12 BankStrategy::Default => {
13 if let Some(source) = &self.source {
14 output.push_str(source);
15 }
16 }
17 BankStrategy::NoTests => {
18 if self.attributes.iter().any(|attr| attr.contains("test")) {
20 return Ok(String::new());
21 }
22 if let Some(source) = &self.source {
23 output.push_str(source);
24 }
25 }
26 BankStrategy::Summary => {
27 if self.visibility == crate::Visibility::Private {
29 return Ok(String::new());
30 }
31 if let Some(sig) = &self.signature {
33 output.push_str(sig);
34 output.push_str(" ...");
35 } else if let Some(source) = &self.source {
36 if let Some(idx) = source.find(':') {
38 output.push_str(&source[0..=idx]);
39 output.push_str(" ...");
40 } else {
41 output.push_str(source);
43 }
44 }
45 }
46 }
47
48 Ok(output)
49 }
50}
51
52impl PythonFormatter for StructUnit {
53 fn format_python(&self, strategy: BankStrategy) -> Result<String> {
54 let mut output = String::new();
55
56 match strategy {
57 BankStrategy::Default => {
58 if let Some(source) = &self.source {
59 output.push_str(source);
60 }
61 for method in &self.methods {
63 output.push_str("\n ");
64 output.push_str(&method.format_python(strategy)?);
65 }
66 }
67 BankStrategy::NoTests => {
68 if let Some(source) = &self.source {
69 output.push_str(source);
70 }
71 for method in &self.methods {
73 if !method.attributes.iter().any(|attr| attr.contains("test")) {
74 output.push_str("\n ");
75 output.push_str(&method.format_python(strategy)?);
76 }
77 }
78 }
79 BankStrategy::Summary => {
80 if self.visibility == crate::Visibility::Private {
82 return Ok(String::new());
83 }
84 if let Some(source) = &self.source {
85 if let Some(idx) = source.find(':') {
87 output.push_str(&source[0..=idx]);
88 output.push('\n');
89 }
90 }
91 for method in &self.methods {
93 if method.visibility == crate::Visibility::Public {
94 output.push_str(" ");
95 output.push_str(&method.format_python(strategy)?);
96 output.push('\n');
97 }
98 }
99 }
100 }
101
102 Ok(output)
103 }
104}
105
106impl PythonFormatter for ModuleUnit {
107 fn format_python(&self, strategy: BankStrategy) -> Result<String> {
108 let mut output = String::new();
109
110 match strategy {
111 BankStrategy::Default => {
112 if let Some(source) = &self.source {
113 output.push_str(source);
114 output.push_str("\n\n");
115 }
116 }
117 BankStrategy::NoTests => {
118 if self.attributes.iter().any(|attr| attr.contains("test_"))
120 || self.name.starts_with("test_")
121 {
122 return Ok(String::new());
123 }
124 for decl in &self.declares {
126 output.push_str(&decl.source);
127 output.push('\n');
128 }
129 for function in &self.functions {
131 let formatted = function.format_python(strategy)?;
132 if !formatted.is_empty() {
133 output.push_str(&formatted);
134 output.push_str("\n\n");
135 }
136 }
137 for class in &self.structs {
138 let formatted = class.format_python(strategy)?;
139 if !formatted.is_empty() {
140 output.push_str(&formatted);
141 output.push_str("\n\n");
142 }
143 }
144 }
145 BankStrategy::Summary => {
146 if self.visibility == crate::Visibility::Private {
148 return Ok(String::new());
149 }
150 for function in &self.functions {
152 if function.visibility == crate::Visibility::Public {
153 let formatted = function.format_python(strategy)?;
154 if !formatted.is_empty() {
155 output.push_str(&formatted);
156 output.push('\n');
157 }
158 }
159 }
160 for class in &self.structs {
161 if class.visibility == crate::Visibility::Public {
162 let formatted = class.format_python(strategy)?;
163 if !formatted.is_empty() {
164 output.push_str(&formatted);
165 output.push('\n');
166 }
167 }
168 }
169 }
170 }
171
172 Ok(output)
173 }
174}
175
176impl PythonFormatter for FileUnit {
177 fn format_python(&self, strategy: BankStrategy) -> Result<String> {
178 let mut output = String::new();
179
180 match strategy {
181 BankStrategy::Default => {
182 if let Some(source) = &self.source {
183 output.push_str(source);
184 }
185 }
186 BankStrategy::NoTests | BankStrategy::Summary => {
187 for decl in &self.declares {
189 output.push_str(&decl.source);
190 output.push('\n');
191 }
192
193 for module in &self.modules {
195 let formatted = module.format_python(strategy)?;
196 if !formatted.is_empty() {
197 output.push_str(&formatted);
198 output.push_str("\n\n");
199 }
200 }
201
202 for function in &self.functions {
204 let formatted = function.format_python(strategy)?;
205 if !formatted.is_empty() {
206 output.push_str(&formatted);
207 output.push_str("\n\n");
208 }
209 }
210
211 for class in &self.structs {
213 let formatted = class.format_python(strategy)?;
214 if !formatted.is_empty() {
215 output.push_str(&formatted);
216 output.push_str("\n\n");
217 }
218 }
219 }
220 }
221
222 Ok(output)
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use crate::{parser::FieldUnit, *};
229
230 fn create_test_function(name: &str, is_public: bool, has_test_attr: bool) -> FunctionUnit {
232 let mut attrs = Vec::new();
233 if has_test_attr {
234 attrs.push("@pytest.mark.test".to_string());
235 }
236
237 FunctionUnit {
238 name: name.to_string(),
239 attributes: attrs,
240 visibility: if is_public {
241 Visibility::Public
242 } else {
243 Visibility::Private
244 },
245 doc: Some(format!("Documentation for {}", name)),
246 signature: Some(format!("def {}():", name)),
247 body: Some(" pass".to_string()),
248 source: Some(format!("def {}():\n pass", name)),
249 }
250 }
251
252 fn create_test_class(name: &str, is_public: bool) -> StructUnit {
254 let mut methods = Vec::new();
255 methods.push(create_test_function(
256 &format!("{}_method", name.to_lowercase()),
257 true,
258 false,
259 ));
260 methods.push(create_test_function(
262 &format!("_{}_private_method", name.to_lowercase()),
263 false,
264 false,
265 ));
266
267 StructUnit {
268 name: name.to_string(),
269 head: format!("class {}", name),
270 attributes: Vec::new(),
271 visibility: if is_public {
272 Visibility::Public
273 } else {
274 Visibility::Private
275 },
276 doc: Some(format!("Documentation for {}", name)),
277 methods,
278 source: Some(format!("class {}:\n pass", name)),
279 fields: Vec::new(),
280 }
281 }
282
283 fn create_test_module(name: &str, is_public: bool, is_test: bool) -> ModuleUnit {
285 let functions = vec![
286 create_test_function("module_function", true, false),
287 create_test_function("_module_private_function", false, false),
289 ];
290
291 let structs = vec![create_test_class("ModuleClass", true)];
292
293 let mut attributes = Vec::new();
294 if is_test {
295 attributes.push("test_".to_string());
296 }
297
298 let mut declares = Vec::new();
300 declares.push(DeclareStatements {
301 source: "from typing import List, Dict".to_string(),
302 kind: DeclareKind::Import,
303 });
304
305 ModuleUnit {
306 name: name.to_string(),
307 attributes,
308 doc: Some(format!("Documentation for module {}", name)),
309 visibility: if is_public {
310 Visibility::Public
311 } else {
312 Visibility::Private
313 },
314 functions,
315 structs,
316 traits: Vec::new(),
317 impls: Vec::new(),
318 submodules: Vec::new(),
319 declares,
320 source: Some(format!("# Module {}", name)),
321 }
322 }
323
324 #[test]
325 fn test_function_formatter_default() {
326 let function = create_test_function("test_function", true, false);
327 let formatted = function
328 .format(&BankStrategy::Default, LanguageType::Python)
329 .unwrap();
330 assert!(formatted.contains("def test_function():"));
331 assert!(formatted.contains("pass"));
332 }
333
334 #[test]
335 fn test_function_formatter_no_tests() {
336 let function = create_test_function("regular_function", true, false);
338 let formatted = function
339 .format(&BankStrategy::NoTests, LanguageType::Python)
340 .unwrap();
341 assert!(formatted.contains("def regular_function():"));
342 assert!(formatted.contains("pass"));
343
344 let test_function = create_test_function("test_function", true, true);
346 let formatted = test_function
347 .format(&BankStrategy::NoTests, LanguageType::Python)
348 .unwrap();
349 assert!(formatted.is_empty());
350 }
351
352 #[test]
353 fn test_function_formatter_summary() {
354 let public_function = create_test_function("public_function", true, false);
356 let formatted = public_function
357 .format(&BankStrategy::Summary, LanguageType::Python)
358 .unwrap();
359 assert!(formatted.contains("def public_function():"));
360 assert!(formatted.contains("..."));
361 assert!(!formatted.contains("pass"));
362
363 let private_function = create_test_function("_private_function", false, false);
365 let formatted = private_function
366 .format(&BankStrategy::Summary, LanguageType::Python)
367 .unwrap();
368 assert!(formatted.is_empty());
369 }
370
371 #[test]
372 fn test_class_formatter_default() {
373 let class_unit = create_test_class("TestClass", true);
374 let formatted = class_unit
375 .format(&BankStrategy::Default, LanguageType::Python)
376 .unwrap();
377 assert!(formatted.contains("class TestClass:"));
378 assert!(formatted.contains("pass"));
379 }
380
381 #[test]
382 fn test_class_formatter_summary() {
383 let mut public_class = create_test_class("PublicClass", true);
385
386 let field = FieldUnit {
388 name: "field".to_string(),
389 doc: Some("Field documentation".to_string()),
390 attributes: vec![],
391 source: Some("field = None".to_string()),
392 };
393 public_class.fields.push(field);
394
395 let formatted = public_class
396 .format(&BankStrategy::Summary, LanguageType::Python)
397 .unwrap();
398
399 assert!(
400 formatted.contains("class PublicClass:"),
401 "Should include class definition"
402 );
403 assert!(formatted.contains("field = None"), "Should include fields");
404 assert!(
405 formatted.contains("def publicclass_method"),
406 "Should include public methods"
407 );
408 assert!(
409 !formatted.contains("def _publicclass_private_method"),
410 "Should not include private methods"
411 );
412
413 let private_class = create_test_class("_PrivateClass", false);
415 let formatted = private_class
416 .format(&BankStrategy::Summary, LanguageType::Python)
417 .unwrap();
418 assert!(formatted.is_empty());
419 }
420
421 #[test]
422 fn test_module_formatter_default() {
423 let module = create_test_module("test_module", true, false);
424 let formatted = module
425 .format(&BankStrategy::Default, LanguageType::Python)
426 .unwrap();
427 assert!(formatted.contains("# Module test_module"));
428 }
429
430 #[test]
431 fn test_module_formatter_no_tests() {
432 let module = create_test_module("regular_module", true, false);
434 let formatted = module
435 .format(&BankStrategy::NoTests, LanguageType::Python)
436 .unwrap();
437 assert!(formatted.contains("def module_function"));
439 assert!(formatted.contains("class ModuleClass"));
440 assert!(formatted.contains("from typing import List, Dict"));
441 assert!(formatted.contains("def _module_private_function")); let test_module = create_test_module("test_module", true, true);
445 let formatted_test = test_module
446 .format(&BankStrategy::NoTests, LanguageType::Python)
447 .unwrap();
448 assert!(!formatted_test.is_empty()); assert!(formatted_test.contains("def module_function")); assert!(formatted_test.contains("class ModuleClass"));
451 }
452
453 #[test]
454 fn test_module_formatter_summary() {
455 let public_module = create_test_module("public_module", true, false);
457 let formatted = public_module
458 .format(&BankStrategy::Summary, LanguageType::Python)
459 .unwrap();
460 assert!(formatted.contains("def module_function():"));
461 assert!(formatted.contains("..."));
462 assert!(!formatted.contains("pass"));
463
464 let private_module = create_test_module("_private_module", false, false);
466 let formatted = private_module
467 .format(&BankStrategy::Summary, LanguageType::Python)
468 .unwrap();
469 assert!(formatted.is_empty());
470 }
471}