ferment_sys/
formatter.rs

1use std::collections::{HashMap, HashSet};
2use std::fmt::{Display, Formatter, Write};
3use indexmap::IndexMap;
4use proc_macro2::{Spacing, TokenTree};
5use quote::{quote, ToTokens};
6use syn::{Attribute, Ident, ItemUse, Path, Signature, Type};
7use crate::composable::{GenericBoundsModel, TraitModelPart1, TraitDecompositionPart1, TraitTypeModel};
8use crate::context::{GlobalContext, ScopeChain, TypeChain};
9use crate::kind::{MixinKind, ObjectKind};
10use crate::tree::{ScopeTreeID, ScopeTreeExportItem, ScopeTreeItem};
11
12#[allow(unused)]
13pub fn format_imported_set(dict: &HashSet<ItemUse>) -> String {
14    let debug_imports = dict.iter().map(|i| {
15        i.to_token_stream()
16    }).collect::<Vec<_>>();
17    let all = quote!(#(#debug_imports,)*);
18    all.to_string()
19}
20
21#[allow(unused)]
22pub fn format_scope_refinement(dict: &[(ScopeChain, IndexMap<Type, ObjectKind>)]) -> String {
23    let mut iter = dict.iter()
24        .map(|(scope, types)|
25            format!("\t{}: \n\t\t{}", format_token_stream(scope.self_path_ref()), types.iter().map(scope_type_conversion_pair).collect::<Vec<_>>()
26                .join("\n\t")))
27        .collect::<Vec<String>>();
28    iter.sort();
29    iter.join("\n")
30
31}
32
33#[allow(unused)]
34pub fn format_types(dict: &HashSet<Type>) -> String {
35    dict.iter()
36        // .map(|item| format_token_stream(item))
37        .map(|item| item.to_token_stream().to_string())
38        .collect::<Vec<_>>()
39        .join("\n\n")
40}
41
42#[allow(unused)]
43pub fn format_mixin_kinds(dict: &IndexMap<MixinKind, HashSet<Option<Attribute>>>) -> String {
44    dict.iter()
45        .map(|(item, attrs)| format!("{}:\t {}", item, format_unique_attrs(attrs)))
46        .collect::<Vec<_>>()
47        .join("\n\t")
48}
49#[allow(unused)]
50pub fn format_mixin_conversions(dict: &IndexMap<GenericBoundsModel, HashSet<Option<Attribute>>>) -> String {
51    dict.iter()
52        .map(|(item, attrs)| format!("{}:\n\t {}", item, format_unique_attrs(attrs)))
53        .collect::<Vec<_>>()
54        .join("\n\t")
55}
56
57#[allow(unused)]
58pub fn format_unique_attrs(dict: &HashSet<Option<Attribute>>) -> String {
59    dict.iter()
60        .map(|item| item.as_ref().map_or("[None]".to_string(), |a| a.to_token_stream().to_string()))
61        .collect::<Vec<_>>()
62        .join("\n\t")
63}
64
65pub fn format_attrs(dict: &[Attribute]) -> String {
66    dict.iter()
67        .map(|item| item.to_token_stream().to_string())
68        .collect::<Vec<_>>()
69        .join("\n\t")
70}
71
72#[allow(unused)]
73pub fn format_imports(dict: &IndexMap<ScopeChain, IndexMap<Path, Path>>) -> String {
74    let vec = scope_imports_dict(dict);
75    let expanded = quote!(#(#vec),*);
76    expanded.to_string()
77}
78
79#[allow(unused)]
80pub fn format_tree_exported_dict(dict: &IndexMap<ScopeTreeID, ScopeTreeExportItem>) -> String {
81    dict.iter()
82        .map(|(ident, tree_item)| format!("{}: {}", ident, tree_item))
83        .collect::<Vec<_>>()
84        .join("\n\n")
85}
86
87#[allow(unused)]
88pub fn format_tree_item_dict(dict: &IndexMap<ScopeTreeID, ScopeTreeItem>) -> String {
89    dict.iter()
90        .map(|(ident, tree_item)| format!("\t{}: {:?}", ident, tree_item))
91        .collect::<Vec<_>>()
92        .join("\n\n")
93}
94
95#[allow(unused)]
96pub fn scope_type_conversion_pair(dict: (&Type, &ObjectKind)) -> String {
97    format!("\t{}: {}", dict.0.to_token_stream(), dict.1)
98    // format!("\t{}: {}", format_token_stream(dict.0), dict.1)
99}
100
101#[allow(unused)]
102pub fn refinement_pair(dict: (&Type, &Vec<ObjectKind>)) -> String {
103    format!("\t{}: \n\t\t{}", dict.0.to_token_stream(), dict.1.iter().map(|i| i.to_string()).collect::<Vec<_>>()
104        .join("\n\t"))
105    // format!("\t{}: {}", format_token_stream(dict.0), dict.1)
106}
107// #[allow(unused)]
108// pub fn scope_refinement_pair(dict: &(&ScopeChain, HashMap<TypeHolder, ObjectKind>)) -> String {
109//     format!("\t{}: \n\t\t{}", dict.0.to_token_stream(), dict.1.iter().map(scope_type_conversion_pair).collect::<Vec<_>>()
110//         .join("\n\t"))
111//     // format!("\t{}: {}", format_token_stream(dict.0), dict.1)
112// }
113
114#[allow(unused)]
115pub fn ident_type_conversion_pair(dict: (&Ident, &Type)) -> String {
116    format!("\t{}: {}", format_token_stream(dict.0), format_token_stream(dict.1))
117}
118
119#[allow(unused)]
120pub fn ident_signature_conversion_pair(dict: (&Ident, &Signature)) -> String {
121    format!("\t{}: {}", format_token_stream(dict.0), format_token_stream(dict.1))
122}
123
124#[allow(unused)]
125pub fn ident_trait_type_decomposition_conversion_pair(dict: (&Ident, &TraitTypeModel)) -> String {
126    format!("\t{}: {}", format_token_stream(dict.0), {
127        let TraitTypeModel { ident, trait_bounds } = dict.1;
128        quote!(#ident: [bounds: #(#trait_bounds)*])
129    })
130}
131fn format_ident_path_pair(pair: (&Path, &Path)) -> String {
132    format!("\t{}: {}", format_token_stream(pair.0), format_token_stream(pair.1))
133}
134
135pub fn format_path_vec(vec: &[Path]) -> String {
136    vec.iter().map(|p| p.to_token_stream().to_string()).collect::<Vec<_>>().join(",")
137}
138pub fn format_obj_vec(vec: &[ObjectKind]) -> String {
139    vec.iter().map(|p| p.to_token_stream().to_string()).collect::<Vec<_>>().join(",")
140}
141
142#[allow(unused)]
143pub fn type_vec_path_conversion_pair(pair: (&Type, &Vec<Path>)) -> String {
144    format!("\t{}: [{}]", format_token_stream(pair.0), format_path_vec(pair.1))
145}
146#[allow(unused)]
147pub fn type_vec_obj_conversion_pair(pair: (&Type, &Vec<ObjectKind>)) -> String {
148    format!("\t{}: [{}]", format_token_stream(pair.0), format_obj_vec(pair.1))
149}
150#[allow(unused)]
151pub fn format_predicates_dict(vec: &HashMap<Type, Vec<Path>>) -> String {
152    vec.iter()
153        .map(type_vec_path_conversion_pair)
154        .collect::<Vec<_>>()
155        .join(",")
156}
157#[allow(unused)]
158pub fn format_predicates_obj_dict(vec: &IndexMap<Type, Vec<ObjectKind>>) -> String {
159    vec.iter()
160        .map(type_vec_obj_conversion_pair)
161        .collect::<Vec<_>>()
162        .join(",")
163}
164
165#[allow(unused)]
166fn format_generic_bounds_pair(pair: (&Type, &Vec<Path>)) -> String {
167    format!("\t{}: [{}]", format_token_stream(pair.0), format_path_vec(pair.1))
168}
169
170fn format_ident_trait_pair(pair: (&Ident, &TraitModelPart1)) -> String {
171    let implementors = &pair.1.implementors;
172    format!("\t{}: {}: [{}]", format_token_stream(pair.0), "...", quote!(#(#implementors),*))
173}
174
175#[allow(unused)]
176pub fn format_types_dict(dict: &IndexMap<Type, ObjectKind>) -> String {
177    types_dict(dict)
178        .join("\n")
179}
180#[allow(unused)]
181pub fn format_types_to_refine(dict: &IndexMap<Type, Vec<ObjectKind>>) -> String {
182    let mut iter = dict.iter()
183        .map(refinement_pair)
184        .collect::<Vec<String>>();
185    iter.sort();
186    iter.join("\n")
187}
188
189#[allow(unused)]
190pub fn format_ident_types_dict(dict: &IndexMap<Ident, Type>) -> String {
191    ident_types_dict(dict)
192        .join("\n")
193}
194
195#[allow(unused)]
196pub fn format_scope_types_dict(dict: &HashMap<ScopeChain, TypeChain>) -> String {
197    dict.iter().map(|(scope, tc)| {
198        format!("{}: \n\t{}", scope.fmt_short(), format_types_dict(&tc.inner))
199    }).collect::<Vec<_>>()
200        .join("\n")
201}
202//
203#[allow(unused)]
204pub fn format_used_traits(dict: &IndexMap<ScopeChain, IndexMap<Ident, TraitModelPart1>>) -> String {
205    scope_traits_dict(dict).join("\n")
206}
207
208// fn format_fn_signature_decomposition(sig: &FnSignatureDecomposition) -> String {
209//     let FnSignatureDecomposition { is_async, ident, scope, return_type, arguments } = sig;
210// }
211
212pub fn format_token_stream<TT: ToTokens>(token_stream: TT) -> String {
213    let token_stream = token_stream.into_token_stream();
214    let mut formatted_string = String::new();
215    let mut space_needed = false;
216    let mut inside_angle_brackets = 0;
217    let mut inside_round_brackets = 0;
218    // let mut inside_square_brackets = 0;
219    let mut last_token_was_ampersand = false;
220    let mut last_token_was_comma = false;
221    // let mut last_token_was_sq_bracket = false;
222    // let mut last_token_was_round_bracket = false;
223    for token in token_stream {
224        if last_token_was_comma {
225            formatted_string.push(' ');
226        }
227        last_token_was_comma = false;
228        match token {
229            TokenTree::Ident(ident) => {
230                // Check for 'mut' or lifetime after '&'
231                if last_token_was_ampersand && (ident == "mut" || ident.to_string().starts_with('\'')) {
232                    formatted_string.pop(); // Remove the space after '&'
233                } else if space_needed {
234                    formatted_string.push(' ');
235                }
236                formatted_string.push_str(&ident.to_string());
237                space_needed = true;
238                last_token_was_ampersand = false;
239            }
240            TokenTree::Punct(punct) => {
241                match punct.as_char() {
242                    ';' => {
243                        formatted_string.push(';');
244                        space_needed = true;
245                    }
246                    ':' => {
247                        formatted_string.push(':');
248                        space_needed = false;
249                    }
250                    '(' => {
251                        inside_round_brackets += 1;
252                        formatted_string.push('(');
253                        space_needed = false;
254                    }
255                    ')' => {
256                        inside_round_brackets -= 1;
257                        formatted_string.push(')');
258                        space_needed = true;
259                    }
260                    '<' => {
261                        inside_angle_brackets += 1;
262                        formatted_string.push('<');
263                        space_needed = false;
264                    }
265                    '>' => {
266                        inside_angle_brackets -= 1;
267                        formatted_string.push('>');
268                        space_needed = true;
269                    }
270                    ',' => {
271                        formatted_string.push(',');
272                        last_token_was_comma = true;
273                        space_needed = true; // Add space after comma
274                    }
275                    '&' => {
276                        formatted_string.push('&');
277                        last_token_was_ampersand = true;
278                        space_needed = false;
279                    }
280                    _ => {
281                        if space_needed {
282                            formatted_string.push(' ');
283                        }
284                        formatted_string.push(punct.as_char());
285                        space_needed = punct.spacing() == Spacing::Alone;
286                    }
287                }
288            }
289            TokenTree::Literal(literal) => {
290                if space_needed {
291                    formatted_string.push(' ');
292                }
293                formatted_string.push_str(&literal.to_string());
294                space_needed = true;
295                last_token_was_ampersand = false;
296            }
297            TokenTree::Group(group) => {
298                if space_needed && (inside_angle_brackets == 0 || inside_round_brackets == 0) {
299                    formatted_string.push(' ');
300                }
301                formatted_string.push_str(&format_token_stream(group.stream()));
302                space_needed = true;
303                last_token_was_ampersand = false;
304            }
305        }
306    }
307
308    formatted_string
309}
310
311pub fn imports_dict(dict: &IndexMap<Path, Path>) -> Vec<String> {
312    dict.iter()
313        .map(format_ident_path_pair)
314        .collect()
315}
316
317#[allow(unused)]
318pub fn generic_bounds_dict(dict: &IndexMap<Type, Vec<Path>>) -> Vec<String> {
319    dict.iter()
320        .map(format_generic_bounds_pair)
321        .collect()
322}
323#[allow(unused)]
324pub fn format_generic_scope_chain(dict: &IndexMap<ObjectKind, Vec<ObjectKind>>) -> String {
325    dict.iter()
326        .map(|(bounded_ty, bounds)| format!("{}: {}", bounded_ty.to_token_stream(), format_obj_vec(bounds)))
327        .collect::<Vec<_>>()
328        .join(", ")
329}
330
331pub fn types_dict(dict: &IndexMap<Type, ObjectKind>) -> Vec<String> {
332    let mut iter = dict.iter()
333        .map(scope_type_conversion_pair)
334        .collect::<Vec<String>>();
335    iter.sort();
336    iter
337}
338fn ident_signatures_dict(dict: &IndexMap<Ident, Signature>) -> Vec<String> {
339    let mut iter = dict.iter()
340        .map(ident_signature_conversion_pair)
341        .collect::<Vec<String>>();
342    iter.sort();
343    iter
344}
345
346
347fn ident_trait_type_decomposition_dict(dict: &IndexMap<Ident, TraitTypeModel>) -> Vec<String> {
348    let mut iter = dict.iter()
349        .map(ident_trait_type_decomposition_conversion_pair)
350        .collect::<Vec<String>>();
351    iter.sort();
352    iter
353}
354
355fn ident_types_dict(dict: &IndexMap<Ident, Type>) -> Vec<String> {
356    let mut iter = dict.iter()
357        .map(ident_type_conversion_pair)
358        .collect::<Vec<String>>();
359    iter.sort();
360    iter
361}
362
363fn traits_dict(dict: &IndexMap<Ident, TraitModelPart1>) -> Vec<String> {
364    let mut iter = dict.iter()
365        .map(format_ident_trait_pair)
366        .collect::<Vec<String>>();
367    iter.sort();
368    iter
369}
370
371
372#[allow(unused)]
373fn nested_scope_dict<K, K2, V2, F: Fn(&K, &IndexMap<K2, V2>) -> String>(dict: &IndexMap<K, IndexMap<K2, V2>>, mapper: F) -> Vec<String> {
374    let mut iter = dict.iter()
375        .map(|(key, value)| mapper(key, value))
376        .collect::<Vec<String>>();
377    iter.sort();
378    iter
379}
380
381fn format_scope_dict<K2, V2, F: Fn(&IndexMap<K2, V2>) -> Vec<String>>(dict: &IndexMap<ScopeChain, IndexMap<K2, V2>>, mapper: F) -> Vec<String>  {
382    let mut iter = dict.iter()
383        .filter_map(|(scope, sub_dict)| {
384            let lines = mapper(sub_dict);
385            (!lines.is_empty()).then(|| format!("\t{}:\n\t\t{}", scope.fmt_short(), lines.join("\n\t\t")))
386        })
387        .collect::<Vec<String>>();
388    iter.sort();
389    iter
390}
391
392pub fn scope_imports_dict(dict: &IndexMap<ScopeChain, IndexMap<Path, Path>>) -> Vec<String> {
393    format_scope_dict(dict, imports_dict)
394}
395
396#[allow(unused)]
397pub fn scope_generics_dict(dict: &IndexMap<ScopeChain, IndexMap<Type, Vec<Path>>>) -> Vec<String> {
398    format_scope_dict(dict, generic_bounds_dict)
399}
400
401
402fn scope_traits_dict(dict: &IndexMap<ScopeChain, IndexMap<Ident, TraitModelPart1>>) -> Vec<String> {
403    format_scope_dict(dict, traits_dict)
404}
405
406
407
408fn traits_impl_dict(dict: &HashMap<ScopeChain, Vec<Path>>) -> Vec<String> {
409    let mut iter = dict.iter()
410        .filter_map(|(key, value)| {
411            let scopes = quote!(#(#value),*);
412            (!value.is_empty()).then(|| format!("\t{}:\n\t\t{}", format_token_stream(key), format_token_stream(&scopes)))
413        })
414        .collect::<Vec<String>>();
415    iter.sort();
416    iter
417}
418
419fn format_complex_obj(vec: Vec<Vec<String>>) -> String {
420    vec.into_iter()
421        .flatten()
422        .collect::<Vec<String>>()
423        .join("\n\t")
424}
425
426pub fn format_global_context(context: &GlobalContext) -> String {
427    let mut sections: Vec<Vec<String>> = Vec::new();
428
429    // Types: always include
430    sections.push(vec!["-- types:".to_string(), context.scope_register.to_string()]);
431
432    // Traits: include only if non-empty
433    let traits = scope_traits_dict(&context.traits.inner);
434    if !traits.is_empty() {
435        sections.push(vec!["-- traits:".to_string()]);
436        sections.push(traits);
437    }
438
439    // Traits impl: include only if non-empty
440    let impls = traits_impl_dict(&context.traits.used_traits_dictionary);
441    if !impls.is_empty() {
442        sections.push(vec!["-- traits_impl:".to_string()]);
443        sections.push(impls);
444    }
445
446    // Custom: include only if non-empty
447    let custom_str = context.custom.to_string();
448    if !custom_str.trim().is_empty() {
449        sections.push(vec!["-- custom:".to_string(), custom_str]);
450    }
451
452    // Imports: include only if non-empty
453    let imports = scope_imports_dict(&context.imports.inner);
454    if !imports.is_empty() {
455        sections.push(vec!["-- imports:".to_string()]);
456        sections.push(imports);
457    }
458
459    // Generics: include only if non-empty (and per-scope filtered above)
460    let generics = scope_generics_dict(&context.generics.inner);
461    if !generics.is_empty() {
462        sections.push(vec!["-- generics:".to_string()]);
463        sections.push(generics);
464    }
465
466    format_complex_obj(sections)
467}
468
469#[allow(unused)]
470pub fn format_trait_decomposition_part1(dict: &TraitDecompositionPart1) -> String {
471    format_complex_obj(vec![
472        vec!["\n-- ident:".to_string()], vec![format_token_stream(&dict.ident)],
473        vec!["-- consts:".to_string()], ident_types_dict(&dict.consts),
474        vec!["-- methods:".to_string()], ident_signatures_dict(&dict.methods),
475        vec!["-- types:".to_string()], ident_trait_type_decomposition_dict(&dict.types),
476    ])
477}
478
479#[allow(dead_code)]
480pub enum Emoji {
481    Branch,
482    Question,
483    Local,
484    Nothing,
485    Ok,
486    Error,
487    Plus,
488    Node,
489    Folder,
490    File
491}
492
493impl Display for Emoji {
494    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
495        f.write_char(
496        match self {
497            Emoji::Question => '\u{2753}',
498            Emoji::Branch => '\u{1D30E}',
499            Emoji::Local => '\u{1F501}',
500            Emoji::Ok => '\u{2705}',
501            Emoji::Error => '\u{274C}',
502            Emoji::Nothing => '\u{1F502}',
503            Emoji::Plus => '\u{271A}',
504            Emoji::Node => '\u{1F491}',
505            Emoji::Folder => '\u{1f4c1}',
506            Emoji::File => '\u{1f4c4}'
507        })
508    }
509}
510
511#[macro_export]
512macro_rules! nprint {
513    ($counter:expr, $emoji:expr, $($arg:tt)*) => {
514        //println!("cargo:warning={}", format!("{}{} {}", " ".repeat($counter*2), $emoji, format!($($arg)*)))
515
516        // log::warn!("{}", ansi_term::Colour::Green.paint(format!("{}{} {}", " ".repeat($counter*2), $emoji, format!($($arg)*))))
517        //ansi_term::Colour::Green.paint(format!("{}{} {}", " ".repeat($counter*2), $emoji, format!($($arg)*)))
518        //println!("{}{} {}", " ".repeat($counter*2), $emoji, format!($($arg)*));
519    };
520}
521
522#[macro_export]
523macro_rules! print_phase {
524    ($label:expr, $($arg:tt)*) => {
525        println!("\n########################################################################################################################");
526        println!("# {}", $label);
527        println!("########################################################################################################################");
528        println!("{}", format!($($arg)*));
529        println!("########################################################################################################################\n");
530    }
531}