workspacer_consolidate/
lib.rs

1// ---------------- [ File: workspacer-consolidate/src/lib.rs ]
2#[macro_use] mod imports; use imports::*;
3
4x!{compute_effective_range}
5x!{formatting}
6x!{consolidate_crate_interface}
7x!{consolidated_crate_interface}
8x!{consolidated_item}
9x!{consolidation_options}
10x!{crate_interface_item_serde}
11x!{crate_interface_item}
12x!{gather_all_attrs}
13x!{gather_assoc_type_aliases}
14x!{gather_crate_items}
15x!{gather_fn_item}
16x!{gather_impl_methods}
17x!{gather_items_in_node}
18x!{gather_module}
19x!{generate_impl_signature}
20x!{guess_is_function}
21x!{has_cfg_test_attr}
22x!{impl_block_interface}
23x!{interstitial_segment}
24x!{is_in_test_module}
25x!{leading_spaces}
26x!{maybe_build_enum}
27x!{maybe_build_function}
28x!{maybe_build_impl_block_node}
29x!{maybe_build_macro_call}
30x!{maybe_build_macro_rules}
31x!{maybe_build_module}
32x!{maybe_build_struct}
33x!{maybe_build_trait}
34x!{maybe_build_type_alias}
35x!{merge_doc_attrs}
36x!{merge}
37x!{module_interface}
38x!{skip_checks}
39x!{trim_to_60}
40x!{try_cast_and_build_item}
41
42#[cfg(test)]
43mod test_text_range {
44    use super::*;
45
46    /// Helpers: parse snippet -> gather crate items -> return them
47    fn gather_single_file_items(snippet: &str) -> Vec<ConsolidatedItem> {
48        // 1) Parse the snippet
49        let parse = SourceFile::parse(snippet, Edition::Edition2021);
50        let sf = parse.tree();
51
52        // 2) We'll use a minimal ConsolidationOptions that doesn't skip anything.
53        let opts = ConsolidationOptions::new()
54            .with_private_items()
55            .with_test_items()
56            .with_docs()
57            .with_fn_bodies()
58            .with_fn_bodies_in_tests();
59
60        // 3) Dummy file_path & crate_path
61        let file_path = PathBuf::from("TEST_ONLY_file.rs");
62        let crate_path = PathBuf::from("TEST_ONLY_crate_root");
63
64        // 4) Gather
65        gather_crate_items(&sf, &opts, &file_path, &crate_path)
66    }
67
68    /// A helper to assert that the item’s text_range matches the underlying syntax node’s range.
69    /// For a `CrateInterfaceItem<ast::Fn>`, for example, you can do `ci.item().syntax().text_range()`.
70    /// Then compare that to `ci.text_range()`.
71    fn assert_ranges_match(
72        syntax_range: TextRange,
73        consolidated_range: TextRange,
74        context: &str,
75    ) {
76        assert_eq!(
77            syntax_range, consolidated_range,
78            "Mismatch in text_range for {}. Syntax node range = {:?}, item range = {:?}",
79            context, syntax_range, consolidated_range
80        );
81    }
82
83    #[test]
84    fn test_fn_text_range() {
85        let snippet = r#"
86            fn example_fn() {
87                let x = 10;
88            }
89        "#;
90        let items = gather_single_file_items(snippet);
91
92        // We expect exactly one item => a ConsolidatedItem::Fn
93        assert_eq!(items.len(), 1, "Expected exactly one item (fn). Found: {:?}", items);
94        match &items[0] {
95            ConsolidatedItem::Fn(ci) => {
96                // Compare the syntax node’s range with the stored text_range
97                let syntax_range = ci.item().syntax().text_range();
98                let item_range   = ci.text_range();
99                assert_ranges_match(syntax_range, *item_range, "fn example_fn");
100            },
101            other => panic!("Expected a single fn item, got {:?}", other),
102        }
103    }
104
105    #[test]
106    fn test_struct_text_range() {
107        let snippet = r#"
108            struct MyStruct {
109                field: i32,
110            }
111        "#;
112        let items = gather_single_file_items(snippet);
113        assert_eq!(items.len(), 1, "Expected one struct item");
114        match &items[0] {
115            ConsolidatedItem::Struct(ci) => {
116                let syntax_range = ci.item().syntax().text_range();
117                let item_range   = ci.text_range();
118                assert_ranges_match(syntax_range, *item_range, "struct MyStruct");
119            },
120            other => panic!("Expected struct, got {:?}", other),
121        }
122    }
123
124    #[test]
125    fn test_enum_text_range() {
126        let snippet = r#"
127            enum Color {
128                Red,
129                Green,
130                Blue,
131            }
132        "#;
133        let items = gather_single_file_items(snippet);
134        assert_eq!(items.len(), 1, "Expected one enum item");
135        match &items[0] {
136            ConsolidatedItem::Enum(ci) => {
137                let syntax_range = ci.item().syntax().text_range();
138                assert_ranges_match(syntax_range, *ci.text_range(), "enum Color");
139            },
140            other => panic!("Expected enum, got {:?}", other),
141        }
142    }
143
144    #[test]
145    fn test_trait_text_range() {
146        let snippet = r#"
147            trait Example {
148                fn do_stuff(&self);
149            }
150        "#;
151        let items = gather_single_file_items(snippet);
152        assert_eq!(items.len(), 1);
153        match &items[0] {
154            ConsolidatedItem::Trait(ci) => {
155                let syntax_range = ci.item().syntax().text_range();
156                assert_ranges_match(syntax_range, *ci.text_range(), "trait Example");
157            },
158            other => panic!("Expected trait, got {:?}", other),
159        }
160    }
161
162    #[test]
163    fn test_type_alias_text_range() {
164        let snippet = r#"
165            type AliasA = i64;
166        "#;
167        let items = gather_single_file_items(snippet);
168        assert_eq!(items.len(), 1);
169        match &items[0] {
170            ConsolidatedItem::TypeAlias(ci) => {
171                let syntax_range = ci.item().syntax().text_range();
172                assert_ranges_match(syntax_range, *ci.text_range(), "type AliasA");
173            },
174            other => panic!("Expected type alias, got {:?}", other),
175        }
176    }
177
178    #[test]
179    fn test_macro_rules_text_range() {
180        let snippet = r#"
181            macro_rules! my_macro {
182                () => {};
183            }
184        "#;
185        let items = gather_single_file_items(snippet);
186        assert_eq!(items.len(), 1);
187        match &items[0] {
188            ConsolidatedItem::Macro(ci) => {
189                let syntax_range = ci.item().syntax().text_range();
190                assert_ranges_match(syntax_range, *ci.text_range(), "macro_rules my_macro");
191            },
192            other => panic!("Expected macro_rules, got {:?}", other),
193        }
194    }
195
196    #[test]
197    fn test_module_text_range() {
198        let snippet = r#"
199            mod submod {
200                fn inside() {}
201            }
202        "#;
203        let items = gather_single_file_items(snippet);
204        assert_eq!(items.len(), 1);
205        match &items[0] {
206            ConsolidatedItem::Module(mi) => {
207                // For a ModuleInterface, we do `mi.text_range()`.
208                // There's no `mi.item().syntax()` directly, but we know the underlying RA node is an `ast::Module`.
209                // So we can parse the snippet again or do:
210                //   let mod_node = ast::Module::cast(...) 
211                // if needed. For a quick check, see if the text_range covers "mod submod { ... }".
212                //
213                // We can compare `mi.text_range()` to the entire snippet range or just do:
214                let actual_range = mi.text_range();
215                // You might do some length check:
216                // For example:
217                let text = snippet;
218                let trimmed_len = text.trim().len() as u32; 
219                // We'll just do a basic sanity check that the range length is > 0:
220                assert!(actual_range.len() > 0.into());
221            },
222            other => panic!("Expected module, got {:?}", other),
223        }
224    }
225
226    #[test]
227    fn test_impl_block_text_range() {
228        let snippet = r#"
229            impl SomeTrait for Foo {
230                fn method(&self) {}
231            }
232        "#;
233        let items = gather_single_file_items(snippet);
234        assert_eq!(items.len(), 1);
235        match &items[0] {
236            ConsolidatedItem::ImplBlock(ib) => {
237                let actual_range = ib.text_range();
238                // Possibly you compare with your parse snippet length or do further analysis:
239                assert!(actual_range.len() > 0.into(), "Impl block range should be > 0");
240            },
241            other => panic!("Expected impl block, got {:?}", other),
242        }
243    }
244}