Skip to main content

zyn_core/
debug.rs

1use proc_macro2::TokenStream;
2
3use crate::ast;
4
5pub fn raw(tokens: &TokenStream) -> String {
6    let raw = tokens.to_string();
7    let cleaned = raw
8        .replace(
9            ":: zyn :: proc_macro2 :: TokenStream :: new ()",
10            "TokenStream::new()",
11        )
12        .replace(":: zyn :: proc_macro2 :: TokenStream", "TokenStream")
13        .replace(":: zyn :: quote :: quote !", "quote!")
14        .replace(
15            ":: zyn :: quote :: ToTokens :: to_tokens",
16            "ToTokens::to_tokens",
17        )
18        .replace(":: zyn :: Pipe :: pipe", "Pipe::pipe")
19        .replace(":: zyn :: Render :: render", "Render::render")
20        .replace(":: core :: compile_error !", "compile_error!")
21        .replace(":: core :: result :: Result :: Ok", "Ok")
22        .replace(":: core :: result :: Result :: Err", "Err")
23        .replace("__zyn_ts_0", "output")
24        .replace("__zyn_ts_1", "inner_1")
25        .replace("__zyn_ts_2", "inner_2")
26        .replace("__zyn_ts_3", "inner_3")
27        .replace("__zyn_ts_4", "inner_4")
28        .replace("__zyn_val", "value")
29        .replace("__zyn_rendered", "rendered")
30        .replace("__zyn_expand_result", "result");
31
32    fmt(&cleaned)
33}
34
35pub fn ast(element: &ast::Element) -> String {
36    let mut lines = Vec::new();
37    lines.push("Element [".to_string());
38
39    for node in &element.nodes {
40        let desc = match node {
41            ast::Node::Tokens(t) => format!("  Tokens({:?})", t.stream.to_string()),
42            ast::Node::Interp(_) => "  Interp { ... }".to_string(),
43            ast::Node::At(at) => {
44                let kind = if at.is_if() {
45                    "If"
46                } else if at.is_for() {
47                    "For"
48                } else if at.is_match() {
49                    "Match"
50                } else if at.is_element() {
51                    "Element"
52                } else {
53                    "Unknown"
54                };
55                format!("  At({})", kind)
56            }
57            ast::Node::Group(_) => "  Group { ... }".to_string(),
58        };
59        lines.push(desc);
60    }
61
62    lines.push("]".to_string());
63    lines.join("\n")
64}
65
66pub fn print(tokens: &TokenStream) {
67    let formatted = fmt(&tokens.to_string());
68    eprintln!("zyn::expand! ─── pretty\n{}", formatted);
69}
70
71pub fn fmt(input: &str) -> String {
72    let mut result = String::new();
73    let mut depth: usize = 0;
74    let mut chars = input.chars().peekable();
75
76    while let Some(ch) = chars.next() {
77        match ch {
78            '{' => {
79                result.push('{');
80                result.push('\n');
81                depth += 1;
82                push_indent(&mut result, depth);
83            }
84            '}' => {
85                depth = depth.saturating_sub(1);
86                if result.ends_with("    ") || result.ends_with('\n') {
87                    result.truncate(result.trim_end().len());
88                    result.push('\n');
89                }
90                push_indent(&mut result, depth);
91                result.push('}');
92
93                if chars
94                    .peek()
95                    .is_some_and(|c| *c != ',' && *c != ';' && *c != ')')
96                {
97                    result.push('\n');
98                    push_indent(&mut result, depth);
99                }
100            }
101            ';' => {
102                result.push(';');
103                result.push('\n');
104                push_indent(&mut result, depth);
105            }
106            ' ' if result.ends_with('\n') || result.ends_with("    ") => {}
107            _ => {
108                result.push(ch);
109            }
110        }
111    }
112
113    result.trim().to_string()
114}
115
116fn push_indent(s: &mut String, depth: usize) {
117    for _ in 0..depth {
118        s.push_str("    ");
119    }
120}