Skip to main content

zyn_core/
debug.rs

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