codebank/parser/formatter/
rust.rs

1#[cfg(test)]
2mod tests {
3    use crate::*;
4
5    // Helper to create a test function
6    fn create_test_function(name: &str, is_public: bool, has_test_attr: bool) -> FunctionUnit {
7        let mut attrs = Vec::new();
8        if has_test_attr {
9            attrs.push("#[test]".to_string());
10        }
11
12        FunctionUnit {
13            name: name.to_string(),
14            attributes: attrs,
15            visibility: if is_public {
16                Visibility::Public
17            } else {
18                Visibility::Private
19            },
20            doc: Some(format!("Documentation for {}", name)),
21            signature: Some(format!("fn {}()", name)),
22            body: Some("{ /* function body */ }".to_string()),
23            source: Some(format!("fn {}() {{ /* function body */ }}", name)),
24        }
25    }
26
27    // Helper to create a test struct
28    fn create_test_struct(name: &str, is_public: bool) -> StructUnit {
29        let mut methods = Vec::new();
30        methods.push(create_test_function(
31            &format!("{}_method", name.to_lowercase()),
32            true,
33            false,
34        ));
35        // Add a private method as well
36        methods.push(create_test_function(
37            &format!("{}_private_method", name.to_lowercase()),
38            false,
39            false,
40        ));
41
42        let visibility = if is_public {
43            Visibility::Public
44        } else {
45            Visibility::Private
46        };
47        StructUnit {
48            name: name.to_string(),
49            head: format!("{} struct {}", visibility.as_str(LanguageType::Rust), name),
50            attributes: Vec::new(),
51            visibility,
52            doc: Some(format!("Documentation for {}", name)),
53            fields: Vec::new(),
54            methods,
55            source: Some(format!("struct {} {{ field: i32 }}", name)),
56        }
57    }
58
59    // Helper to create a test module
60    fn create_test_module(name: &str, is_public: bool, is_test: bool) -> ModuleUnit {
61        let functions = vec![
62            create_test_function("module_function", true, false),
63            // Add a private function
64            create_test_function("module_private_function", false, false),
65        ];
66
67        let structs = vec![create_test_struct("ModuleStruct", true)];
68
69        let mut attributes = Vec::new();
70        if is_test {
71            attributes.push("#[cfg(test)]".to_string());
72        }
73
74        // Add declarations
75        let mut declares = Vec::new();
76        declares.push(DeclareStatements {
77            source: "use std::io;".to_string(),
78            kind: DeclareKind::Use,
79        });
80
81        ModuleUnit {
82            name: name.to_string(),
83            attributes,
84            doc: Some(format!("Documentation for module {}", name)),
85            visibility: if is_public {
86                Visibility::Public
87            } else {
88                Visibility::Private
89            },
90            functions,
91            structs,
92            traits: Vec::new(),
93            impls: Vec::new(),
94            submodules: Vec::new(),
95            declares,
96            source: Some(format!("mod {} {{ /* module contents */ }}", name)),
97        }
98    }
99
100    // Helper to create a test impl block, with option for trait implementation
101    fn create_test_impl(is_trait_impl: bool) -> ImplUnit {
102        let methods = vec![
103            // Add both public and private methods
104            create_test_function("public_method", true, false),
105            create_test_function("private_method", false, false),
106        ];
107
108        let (head, source) = if is_trait_impl {
109            (
110                "impl SomeTrait for SomeStruct".to_string(),
111                "impl SomeTrait for SomeStruct { /* impl body */ }".to_string(),
112            )
113        } else {
114            (
115                "impl SomeStruct".to_string(),
116                "impl SomeStruct { /* impl body */ }".to_string(),
117            )
118        };
119
120        ImplUnit {
121            attributes: Vec::new(),
122            doc: Some("Documentation for implementation".to_string()),
123            head,
124            methods,
125            source: Some(source),
126        }
127    }
128
129    // Helper to create a test impl block with only private methods
130    fn create_private_methods_impl() -> ImplUnit {
131        ImplUnit {
132            attributes: Vec::new(),
133            doc: Some("Documentation for implementation with private methods".to_string()),
134            head: "impl StructWithPrivateMethods".to_string(),
135            methods: vec![
136                create_test_function("private_method1", false, false),
137                create_test_function("private_method2", false, false),
138            ],
139            source: Some("impl StructWithPrivateMethods { /* impl body */ }".to_string()),
140        }
141    }
142
143    // Helper to create a test enum
144    fn create_test_enum(name: &str, is_public: bool) -> StructUnit {
145        let visibility = if is_public {
146            Visibility::Public
147        } else {
148            Visibility::Private
149        };
150        let head = format!("{} enum {}", visibility.as_str(LanguageType::Rust), name);
151        let source = format!(
152            "/// Docs for {}\n{} {{
153    VariantA,
154    VariantB(String),
155}}",
156            name, head
157        );
158        StructUnit {
159            name: name.to_string(),
160            head,
161            visibility,
162            doc: Some(format!("Docs for {}", name)),
163            attributes: vec![],
164            fields: vec![], // Variants aren't parsed as fields currently
165            methods: vec![],
166            source: Some(source),
167        }
168    }
169
170    #[test]
171    fn test_function_formatter_default() {
172        let function = create_test_function("test_function", true, false);
173        let formatted = function
174            .format(&BankStrategy::Default, LanguageType::Rust)
175            .unwrap();
176        assert!(formatted.contains("fn test_function()"));
177        assert!(formatted.contains("/* function body */"));
178    }
179
180    #[test]
181    fn test_function_formatter_no_tests() {
182        // Regular function
183        let function = create_test_function("regular_function", true, false);
184        let formatted = function
185            .format(&BankStrategy::NoTests, LanguageType::Rust)
186            .unwrap();
187        assert!(formatted.contains("fn regular_function()"));
188        assert!(formatted.contains("/* function body */"));
189
190        // Test function
191        let test_function = create_test_function("test_function", true, true);
192        let formatted = test_function
193            .format(&BankStrategy::NoTests, LanguageType::Rust)
194            .unwrap();
195        assert!(formatted.is_empty());
196    }
197
198    #[test]
199    fn test_function_formatter_summary() {
200        // Public function
201        let public_function = create_test_function("public_function", true, false);
202        let formatted = public_function
203            .format(&BankStrategy::Summary, LanguageType::Rust)
204            .unwrap();
205        assert!(formatted.contains("fn public_function()"));
206        assert!(!formatted.contains("/* function body */"));
207        assert!(formatted.contains("{ ... }"));
208
209        // Private function
210        let private_function = create_test_function("private_function", false, false);
211        let formatted = private_function
212            .format(&BankStrategy::Summary, LanguageType::Rust)
213            .unwrap();
214        assert!(formatted.is_empty());
215    }
216
217    #[test]
218    fn test_struct_formatter_default() {
219        let struct_unit = create_test_struct("TestStruct", true);
220        let formatted = struct_unit
221            .format(&BankStrategy::Default, LanguageType::Rust)
222            .unwrap();
223        assert!(formatted.contains("struct TestStruct"));
224        assert!(formatted.contains("field: i32"));
225    }
226
227    #[test]
228    fn test_struct_formatter_summary() {
229        // Public struct
230        let mut public_struct = create_test_struct("PublicStruct", true);
231
232        // Add a field to the struct
233        let field = FieldUnit {
234            name: "field".to_string(),
235            doc: Some("Field documentation".to_string()),
236            attributes: vec![],
237            source: Some("pub field: i32".to_string()),
238        };
239        public_struct.fields.push(field);
240
241        let formatted = public_struct
242            .format(&BankStrategy::Summary, LanguageType::Rust)
243            .unwrap();
244
245        assert!(formatted.contains("struct PublicStruct"));
246        assert!(
247            formatted.contains("pub field: i32"),
248            "Summary should include fields"
249        );
250        assert!(
251            formatted.contains("fn publicstruct_method"),
252            "Summary should include public methods"
253        );
254        assert!(
255            !formatted.contains("fn publicstruct_private_method"),
256            "Summary should not include private methods"
257        );
258
259        // Private struct should be skipped
260        let private_struct = create_test_struct("PrivateStruct", false);
261        let formatted = private_struct
262            .format(&BankStrategy::Summary, LanguageType::Rust)
263            .unwrap();
264        assert!(
265            formatted.is_empty(),
266            "Private structs should be skipped in summary mode"
267        );
268    }
269
270    #[test]
271    fn test_module_formatter_default() {
272        let module = create_test_module("test_module", true, false);
273        let formatted = module
274            .format(&BankStrategy::Default, LanguageType::Rust)
275            .unwrap();
276        assert!(formatted.contains("mod test_module"));
277        assert!(formatted.contains("/* module contents */"));
278    }
279
280    #[test]
281    fn test_module_formatter_no_tests() {
282        // Regular module
283        let module = create_test_module("regular_module", true, false);
284        let formatted = module
285            .format(&BankStrategy::NoTests, LanguageType::Rust)
286            .unwrap();
287        assert!(formatted.contains("pub mod regular_module"));
288        assert!(formatted.contains("fn module_function"));
289        assert!(formatted.contains("fn module_private_function"));
290        assert!(formatted.contains("struct ModuleStruct"));
291        assert!(formatted.contains("use std::io;"));
292
293        // Test module
294        let test_module = create_test_module("test_module", true, true);
295        let formatted = test_module
296            .format(&BankStrategy::NoTests, LanguageType::Rust)
297            .unwrap();
298        assert!(formatted.contains("#[cfg(test)]"));
299        assert!(formatted.contains("pub mod test_module"));
300    }
301
302    #[test]
303    fn test_module_formatter_summary() {
304        // Public module
305        let public_module = create_test_module("public_module", true, false);
306        let formatted = public_module
307            .format(&BankStrategy::Summary, LanguageType::Rust)
308            .unwrap();
309        assert!(formatted.contains("pub mod public_module"));
310        assert!(formatted.contains("fn module_function()"));
311        // Functions should only show signatures in summary
312        assert!(!formatted.contains("/* function body */"));
313
314        // Private module
315        let private_module = create_test_module("private_module", false, false);
316        let formatted = private_module
317            .format(&BankStrategy::Summary, LanguageType::Rust)
318            .unwrap();
319        assert!(formatted.is_empty());
320    }
321
322    #[test]
323    fn test_struct_formatter_no_tests() {
324        // Test struct with private methods
325        let struct_unit = create_test_struct("TestStruct", true);
326        let formatted = struct_unit
327            .format(&BankStrategy::NoTests, LanguageType::Rust)
328            .unwrap();
329
330        // Should now just return the source for NoTests mode
331        assert!(formatted.contains("struct TestStruct { field: i32 }"));
332        // Should not contain methods as we're just using the source
333        assert!(!formatted.contains("fn teststruct_method()"));
334        assert!(!formatted.contains("fn teststruct_private_method()"));
335    }
336
337    #[test]
338    fn test_regular_impl_formatter_summary() {
339        // Regular (non-trait) implementation
340        let impl_unit = create_test_impl(false);
341        let formatted = impl_unit
342            .format(&BankStrategy::Summary, LanguageType::Rust)
343            .unwrap();
344
345        // Only public methods should be included in regular impls
346        // Check the head extracted by the parser
347        assert!(formatted.contains("impl SomeStruct"));
348        assert!(formatted.contains("fn public_method"));
349        assert!(!formatted.contains("fn private_method"));
350    }
351
352    #[test]
353    fn test_trait_impl_formatter_summary() {
354        // Trait implementation
355        let impl_unit = create_test_impl(true);
356        let formatted = impl_unit
357            .format(&BankStrategy::Summary, LanguageType::Rust)
358            .unwrap();
359
360        // Both public and private methods should be included in trait impls
361        // Check the head extracted by the parser
362        assert!(formatted.contains("impl SomeTrait for SomeStruct"));
363        assert!(formatted.contains("fn public_method"));
364        assert!(
365            !formatted.contains("fn private_method"),
366            "Private method should be excluded in trait impl summary"
367        );
368        // Check that bodies are summarized
369        assert!(
370            formatted.contains("public_method() { ... }"),
371            "Public method body not summarized"
372        );
373        assert!(
374            !formatted.contains("/* function body */"),
375            "Full function body should not be present"
376        );
377    }
378
379    #[test]
380    fn test_impl_formatter_no_tests() {
381        // Both regular and trait implementation should include all non-test methods in NoTests mode
382        let regular_impl = create_test_impl(false);
383        let formatted = regular_impl
384            .format(&BankStrategy::NoTests, LanguageType::Rust)
385            .unwrap();
386        assert!(formatted.contains("fn public_method"));
387        assert!(formatted.contains("fn private_method"));
388
389        let trait_impl = create_test_impl(true);
390        let formatted = trait_impl
391            .format(&BankStrategy::NoTests, LanguageType::Rust)
392            .unwrap();
393        assert!(formatted.contains("fn public_method"));
394        assert!(formatted.contains("fn private_method"));
395    }
396
397    #[test]
398    fn test_impl_with_only_private_methods_summary() {
399        // Regular impl with only private methods should return empty string in Summary mode
400        let impl_unit = create_private_methods_impl();
401        let formatted = impl_unit
402            .format(&BankStrategy::Summary, LanguageType::Rust)
403            .unwrap();
404
405        // Should be empty since there are no public methods
406        assert!(formatted.is_empty());
407
408        // But in NoTests mode, it should include the private methods
409        let formatted = impl_unit
410            .format(&BankStrategy::NoTests, LanguageType::Rust)
411            .unwrap();
412        assert!(!formatted.is_empty());
413        assert!(formatted.contains("fn private_method1"));
414        assert!(formatted.contains("fn private_method2"));
415    }
416
417    #[test]
418    fn test_file_unit_formatter() {
419        let mut file_unit = FileUnit {
420            path: std::path::PathBuf::from("test_file.rs"),
421            ..Default::default()
422        };
423
424        // Add modules
425        file_unit
426            .modules
427            .push(create_test_module("public_module", true, false));
428        file_unit
429            .modules
430            .push(create_test_module("test_module", true, true));
431
432        // Add functions
433        file_unit
434            .functions
435            .push(create_test_function("public_function", true, false));
436        file_unit
437            .functions
438            .push(create_test_function("private_function", false, false));
439        file_unit
440            .functions
441            .push(create_test_function("test_function", true, true));
442
443        // Add structs
444        file_unit
445            .structs
446            .push(create_test_struct("PublicStruct", true));
447        file_unit
448            .structs
449            .push(create_test_struct("PrivateStruct", false));
450
451        // Test Default strategy
452        file_unit.source = Some("// This is the entire file content".to_string());
453        let formatted = file_unit
454            .format(&BankStrategy::Default, LanguageType::Rust)
455            .unwrap();
456        assert_eq!(formatted, "// This is the entire file content");
457
458        // Test NoTests strategy - test modules and functions should be excluded
459        let formatted = file_unit
460            .format(&BankStrategy::NoTests, LanguageType::Rust)
461            .unwrap();
462        assert!(formatted.contains("pub mod public_module"));
463        assert!(!formatted.contains("fn test_function"));
464        assert!(formatted.contains("fn public_function"));
465        assert!(formatted.contains("fn private_function"));
466        assert!(formatted.contains("struct PublicStruct"));
467        assert!(formatted.contains("struct PrivateStruct"));
468
469        // Test Summary strategy - only public items should be included
470        let formatted = file_unit
471            .format(&BankStrategy::Summary, LanguageType::Rust)
472            .unwrap();
473        assert!(formatted.contains("pub mod public_module"));
474        assert!(!formatted.contains("mod private_module"));
475        assert!(formatted.contains("fn public_function()"));
476        assert!(!formatted.contains("fn private_function"));
477        assert!(formatted.contains("struct PublicStruct"));
478        assert!(!formatted.contains("struct PrivateStruct"));
479    }
480
481    #[test]
482    fn test_file_unit_no_tests_includes_all() {
483        let mut file_unit = FileUnit {
484            path: std::path::PathBuf::from("test_file.rs"),
485            ..Default::default()
486        };
487
488        // Add modules
489        file_unit
490            .modules
491            .push(create_test_module("public_module", true, false));
492        file_unit
493            .modules
494            .push(create_test_module("private_module", false, false));
495        file_unit
496            .modules
497            .push(create_test_module("test_module", true, true));
498
499        // Add functions
500        file_unit
501            .functions
502            .push(create_test_function("public_function", true, false));
503        file_unit
504            .functions
505            .push(create_test_function("private_function", false, false));
506        file_unit
507            .functions
508            .push(create_test_function("test_function", true, true));
509
510        // Add structs
511        file_unit
512            .structs
513            .push(create_test_struct("PublicStruct", true));
514        file_unit
515            .structs
516            .push(create_test_struct("PrivateStruct", false));
517
518        // Add declarations
519        file_unit.declares.push(DeclareStatements {
520            source: "use std::collections::HashMap;".to_string(),
521            kind: DeclareKind::Use,
522        });
523
524        // Test NoTests strategy
525        let formatted = file_unit
526            .format(&BankStrategy::NoTests, LanguageType::Rust)
527            .unwrap();
528
529        // Should include all non-test items regardless of visibility
530        assert!(formatted.contains("pub mod public_module"));
531        assert!(formatted.contains("mod private_module"));
532        assert!(!formatted.contains("fn test_function"));
533        assert!(formatted.contains("fn public_function"));
534        assert!(formatted.contains("fn private_function"));
535        assert!(formatted.contains("struct PublicStruct"));
536        assert!(formatted.contains("struct PrivateStruct"));
537        assert!(formatted.contains("use std::collections::HashMap;"));
538
539        // We now just display struct source in NoTests, not individual methods anymore
540        assert!(!formatted.contains("fn publicstruct_private_method()"));
541    }
542
543    #[test]
544    fn test_enum_formatter_summary() {
545        let public_enum = create_test_enum("PublicEnum", true);
546        let formatted = public_enum
547            .format(&BankStrategy::Summary, LanguageType::Rust)
548            .unwrap();
549
550        // Summary for enums now follows the same pattern as structs
551        assert!(formatted.contains("/// Docs for PublicEnum"));
552        assert!(formatted.contains("pub enum PublicEnum"));
553        // No fields/variants in the enum
554        assert!(!formatted.contains("VariantA,"));
555        assert!(!formatted.contains("VariantB(String),"));
556
557        let private_enum = create_test_enum("PrivateEnum", false);
558        let formatted = private_enum
559            .format(&BankStrategy::Summary, LanguageType::Rust)
560            .unwrap();
561        // Private enums should be omitted entirely in summary
562        assert!(formatted.is_empty());
563    }
564}