1#![allow(clippy::print_stderr, clippy::print_stdout)]
2
3use dampen_core::{HandlerSignature, generate_application, parse};
6use std::fs;
7
8#[derive(clap::Args)]
9pub struct InspectArgs {
10 #[arg(short, long)]
12 file: String,
13
14 #[arg(long)]
16 codegen: bool,
17
18 #[arg(long, default_value = "human")]
20 format: String,
21
22 #[arg(long, default_value = "Model")]
24 model: String,
25
26 #[arg(long, default_value = "Message")]
28 message: String,
29
30 #[arg(long, value_delimiter = ',', num_args = 1..)]
32 handlers: Vec<String>,
33}
34
35pub fn execute(args: &InspectArgs) -> Result<(), String> {
36 let content = fs::read_to_string(&args.file)
38 .map_err(|e| format!("Failed to read file '{}': {}", args.file, e))?;
39
40 let document = parse(&content).map_err(|e| format!("Parse error: {}", e))?;
42
43 if args.codegen {
44 let handler_signatures: Vec<HandlerSignature> = args
46 .handlers
47 .iter()
48 .map(|name| {
49 HandlerSignature {
50 name: name.clone(),
51 param_type: None, returns_command: false,
53 }
54 })
55 .collect();
56
57 let output =
58 generate_application(&document, &args.model, &args.message, &handler_signatures)
59 .map_err(|e| format!("Code generation error: {}", e))?;
60
61 match args.format.as_str() {
62 "json" => {
63 let json = serde_json::json!({
64 "code": output.code,
65 "warnings": output.warnings,
66 });
67 let json_str = serde_json::to_string_pretty(&json)
68 .map_err(|e| format!("JSON serialization error: {}", e))?;
69 println!("{}", json_str);
70 }
71 "human" => {
72 println!("// Generated Rust code from: {}\n", args.file);
73 if !output.warnings.is_empty() {
74 println!("// Warnings:");
75 for warning in &output.warnings {
76 println!("// - {}", warning);
77 }
78 println!();
79 }
80 println!("{}", output.code);
81 }
82 _ => return Err(format!("Unknown format: {}", args.format)),
83 }
84 } else {
85 match args.format.as_str() {
87 "json" => {
88 let json = serde_json::to_string_pretty(&document)
89 .map_err(|e| format!("JSON serialization error: {}", e))?;
90 println!("{}", json);
91 }
92 "human" => {
93 print_ir_tree(&document, 0);
94 }
95 _ => return Err(format!("Unknown format: {}", args.format)),
96 }
97 }
98
99 Ok(())
100}
101
102fn print_ir_tree(doc: &dampen_core::DampenDocument, indent: usize) {
104 let prefix = " ".repeat(indent);
105
106 println!("{}DampenDocument {{", prefix);
107 println!(
108 "{} version: v{}.{}",
109 prefix, doc.version.major, doc.version.minor
110 );
111 println!("{} root:", prefix);
112 print_widget_node(&doc.root, indent + 2);
113 println!("{}}}", prefix);
114}
115
116fn print_widget_node(node: &dampen_core::WidgetNode, indent: usize) {
117 let prefix = " ".repeat(indent);
118
119 println!("{}WidgetNode {{", prefix);
120 println!("{} kind: {:?}", prefix, node.kind);
121
122 if let Some(id) = &node.id {
123 println!("{} id: Some({:?})", prefix, id);
124 }
125
126 if !node.attributes.is_empty() {
128 println!("{} attributes: {{", prefix);
129 for (key, value) in &node.attributes {
130 print!("{} {:?}: ", prefix, key);
131 print_attribute_value(value);
132 println!();
133 }
134 println!("{} }}", prefix);
135 }
136
137 if !node.events.is_empty() {
139 println!("{} events: [", prefix);
140 for event in &node.events {
141 println!(
142 "{} {:?} -> {} (line {}, col {})",
143 prefix, event.event, event.handler, event.span.line, event.span.column
144 );
145 }
146 println!("{} ]", prefix);
147 }
148
149 if !node.children.is_empty() {
151 println!("{} children: [", prefix);
152 for child in &node.children {
153 print_widget_node(child, indent + 2);
154 }
155 println!("{} ]", prefix);
156 }
157
158 println!(
159 "{} span: line {}, column {}",
160 prefix, node.span.line, node.span.column
161 );
162 println!("{}}}", prefix);
163}
164
165fn print_attribute_value(value: &dampen_core::AttributeValue) {
166 match value {
167 dampen_core::AttributeValue::Static(s) => {
168 print!("Static({:?})", s);
169 }
170 dampen_core::AttributeValue::Binding(expr) => {
171 print!("Binding(");
172 print_expr(&expr.expr);
173 print!(")");
174 }
175 dampen_core::AttributeValue::Interpolated(parts) => {
176 print!("Interpolated([");
177 for (i, part) in parts.iter().enumerate() {
178 if i > 0 {
179 print!(", ");
180 }
181 match part {
182 dampen_core::InterpolatedPart::Literal(s) => {
183 print!("Literal({:?})", s);
184 }
185 dampen_core::InterpolatedPart::Binding(expr) => {
186 print!("Binding(");
187 print_expr(&expr.expr);
188 print!(")");
189 }
190 }
191 }
192 print!("])");
193 }
194 }
195}
196
197fn print_expr(expr: &dampen_core::Expr) {
198 match expr {
199 dampen_core::Expr::FieldAccess(fa) => {
200 print!("FieldAccess({})", fa.path.join("."));
201 }
202 dampen_core::Expr::MethodCall(mc) => {
203 print!("MethodCall(");
204 print_expr(&mc.receiver);
205 print!(".{}(", mc.method);
206 for (i, arg) in mc.args.iter().enumerate() {
207 if i > 0 {
208 print!(", ");
209 }
210 print_expr(arg);
211 }
212 print!("))");
213 }
214 dampen_core::Expr::BinaryOp(bo) => {
215 print!("BinaryOp(");
216 print_expr(&bo.left);
217 print!(" {:?} ", bo.op);
218 print_expr(&bo.right);
219 print!(")");
220 }
221 dampen_core::Expr::UnaryOp(uo) => {
222 print!("UnaryOp({:?} ", uo.op);
223 print_expr(&uo.operand);
224 print!(")");
225 }
226 dampen_core::Expr::Conditional(ce) => {
227 print!("Conditional(");
228 print_expr(&ce.condition);
229 print!(" then ");
230 print_expr(&ce.then_branch);
231 print!(" else ");
232 print_expr(&ce.else_branch);
233 print!(")");
234 }
235 dampen_core::Expr::Literal(lit) => match lit {
236 dampen_core::LiteralExpr::String(s) => print!("Literal({:?})", s),
237 dampen_core::LiteralExpr::Integer(i) => print!("Literal({})", i),
238 dampen_core::LiteralExpr::Float(f) => print!("Literal({})", f),
239 dampen_core::LiteralExpr::Bool(b) => print!("Literal({})", b),
240 },
241 dampen_core::Expr::SharedFieldAccess(sa) => {
242 print!("SharedFieldAccess(shared.{})", sa.path.join("."));
243 }
244 }
245}