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