1use proc_macro2::TokenStream;
2
3use crate::ast;
4
5pub 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
36pub 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
68pub fn print(tokens: &TokenStream) {
70 let formatted = fmt(&tokens.to_string());
71 eprintln!("zyn::debug! ─── pretty\n{}", formatted);
72}
73
74pub 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}