1use crate::ReplBootstrapError;
16use colored::Colorize;
17use golem_wasm_ast::analysis::analysed_type::{record, str, u64};
18use golem_wasm_ast::analysis::{AnalysedResourceMode, AnalysedType, NameTypePair, TypeHandle};
19use golem_wasm_rpc::{Value, ValueAndType};
20use rib::*;
21use std::collections::BTreeMap;
22use std::fmt::Display;
23
24pub trait ReplPrinter {
25 fn print_bootstrap_error(&self, error: &ReplBootstrapError) {
26 print_bootstrap_error(error);
27 }
28
29 fn print_clap_parse_error(&self, error: &clap::Error) {
30 println!("{}", error.to_string().red());
31 }
32
33 fn print_custom_error(&self, error: &str) {
34 println!("{} {}", "[message]".red(), error.red());
35 }
36
37 fn print_custom_message(&self, message: &str) {
38 println!("{} {}", "[message]".yellow(), message.cyan());
39 }
40
41 fn print_components_and_exports(&self, exports: &ComponentDependencies) {
42 for (component_dependency_key, component) in &exports.dependencies {
43 let mut indent = Indent::new();
44
45 println!(
46 "{} {}",
47 "📦 Component:".bold().bright_yellow(),
48 component_dependency_key
49 .component_name
50 .bold()
51 .truecolor(180, 180, 180)
52 );
53
54 indent.add();
55
56 if let Some(root_package) = &component_dependency_key.root_package_name {
57 println!(
58 "{} {} {}",
59 indent,
60 "Root Package:".bold().bright_cyan(),
61 root_package.bold().truecolor(180, 180, 180)
62 );
63
64 indent.add();
65 }
66
67 if let Some(root_interface) = &component_dependency_key.root_package_version {
68 println!(
69 "{} {} {}",
70 indent,
71 "Root Package Version:".bold().bright_cyan(),
72 root_interface.bold().truecolor(180, 180, 180)
73 );
74
75 indent.add();
76 }
77
78 print_function_dictionary(&mut indent, component)
79 }
80 }
81
82 fn print_rib_compilation_error(&self, error: &RibCompilationError) {
83 print_rib_compilation_error(error);
84 }
85
86 fn print_rib_result(&self, result: &RibResult) {
87 match result {
88 RibResult::Unit => {
89 println!("{}", "()".yellow());
90 }
91
92 RibResult::Val(value_and_type) => match &value_and_type.value {
93 Value::Handle { uri, resource_id } => {
94 println!("{} {}", "[warn]".magenta(), "the syntax below to show the resource-handle value is only used for display purposes".to_string().white());
95
96 println!();
97
98 let resource = Value::Record(vec![
99 Value::String(uri.to_string()),
100 Value::U64(*resource_id),
101 ]);
102
103 let analysed_type = record(vec![
104 NameTypePair {
105 name: "uri".to_string(),
106 typ: str(),
107 },
108 NameTypePair {
109 name: "resource-id".to_string(),
110 typ: u64(),
111 },
112 ]);
113
114 let result = ValueAndType::new(resource, analysed_type);
115
116 println!("{}", result.to_string().yellow());
117 }
118
119 _ => println!("{}", result.to_string().yellow()),
120 },
121 }
122 }
123
124 fn print_rib_runtime_error(&self, error: &RibRuntimeError) {
125 println!("{} {}", "[runtime error]".red(), error.to_string().white());
126 }
127
128 fn print_wasm_value_type(&self, analysed_type: &AnalysedType) {
129 match analysed_type {
130 AnalysedType::Handle(type_handle) => {
131 let text = display_for_resource_handle(type_handle);
132 println!("{} {}", "[warn]".magenta(), "the syntax below to show the resource-handle type is only used for display purposes".to_string().white());
133
134 println!();
135
136 println!("{}", text.yellow());
137 }
138
139 _ => println!(
140 "{}",
141 wasm_wave::wasm::DisplayType(analysed_type)
142 .to_string()
143 .yellow()
144 ),
145 }
146 }
147}
148
149#[derive(Clone)]
150pub struct DefaultReplResultPrinter;
151
152impl ReplPrinter for DefaultReplResultPrinter {}
153
154pub fn print_function_dictionary(indent: &mut Indent, dict: &FunctionDictionary) {
155 let mut output = String::new();
156
157 let mut hierarchy: BTreeMap<
158 Option<PackageName>,
159 BTreeMap<Option<InterfaceName>, HierarchyNode>,
160 > = BTreeMap::new();
161
162 for (name, ftype) in &dict.name_and_types {
163 match name {
164 FunctionName::Function(func) => {
165 let node = hierarchy
166 .entry(func.package_name.clone())
167 .or_default()
168 .entry(func.interface_name.clone())
169 .or_default();
170 node.functions.push((func.function_name.clone(), ftype));
171 }
172
173 FunctionName::ResourceConstructor(ctor) => {
174 let node = hierarchy
175 .entry(ctor.package_name.clone())
176 .or_default()
177 .entry(ctor.interface_name.clone())
178 .or_default();
179 node.resources
180 .entry(ctor.resource_name.clone())
181 .or_default()
182 .constructor = Some(ftype);
183 }
184
185 FunctionName::ResourceMethod(method) => {
186 let node = hierarchy
187 .entry(method.package_name.clone())
188 .or_default()
189 .entry(method.interface_name.clone())
190 .or_default();
191 node.resources
192 .entry(method.resource_name.clone())
193 .or_default()
194 .methods
195 .push((method.method_name.clone(), ftype));
196 }
197 FunctionName::Variant(_) => {
198 continue;
199 }
200 FunctionName::Enum(_) => {
201 continue;
202 }
203 }
204 }
205
206 for (pkg, interfaces) in hierarchy {
207 match pkg {
208 Some(pkg) => {
209 output.push_str(&format!(
210 "{} {} {}\n",
211 indent,
212 "📦 Package:".bold().bright_yellow(),
213 format!("{}:{}", pkg.namespace, pkg.package_name)
214 .bold()
215 .truecolor(180, 180, 180)
216 ));
217
218 indent.add();
219 }
220 None => {
221 output.push_str(&format!("{}\n", "📦 Global Scope".bold().bright_yellow()));
222 indent.add();
223 }
224 }
225
226 for (iface, node) in interfaces {
227 if let Some(iface) = iface {
228 output.push_str(&format!(
229 "{} {} {}\n",
230 indent,
231 "📄 Interface:".bold().bright_cyan(),
232 iface.name.bold().truecolor(180, 180, 180)
233 ));
234 indent.add();
235 }
236
237 if !node.functions.is_empty() {
238 output.push_str(&format!(
239 "{} {}\n",
240 indent,
241 "🔧 Functions:".bold().bright_green(),
242 ));
243 indent.add();
244 }
245
246 for (fname, ftype) in &node.functions {
247 output.push_str(&format!("{} {}\n", indent, fname.bright_magenta()));
248 indent.add();
249 output.push_str(&format!(
250 "{} ↳ {}: {}\n",
251 indent,
252 "Args".blue(),
253 format_type_list(&ftype.parameter_types).truecolor(180, 180, 180)
254 ));
255 output.push_str(&format!(
256 "{} ↳ {}: {}\n",
257 indent,
258 "Returns".blue(),
259 format_return_type(&ftype.return_type).truecolor(180, 180, 180)
260 ));
261 indent.remove();
262 }
263
264 for (res_name, res) in &node.resources {
265 output.push_str(&format!(
266 "{} {} {}\n",
267 indent,
268 "🧩️ Resource:".bold().bright_yellow(),
269 res_name.truecolor(180, 180, 180)
270 ));
271
272 indent.add();
273
274 if let Some(ftype) = res.constructor {
275 output.push_str(&format!(
276 "{} ↳ {}: {}\n",
277 indent,
278 "Args".blue(),
279 format_type_list(&ftype.parameter_types).truecolor(180, 180, 180)
280 ));
281
282 indent.add();
283
284 output.push_str(&format!(
285 "{} {} \n",
286 indent,
287 "🔧 Methods:".bold().bright_green(),
288 ));
289
290 indent.add();
291
292 for (mname, mtype) in &res.methods {
293 output.push_str(&format!("{} {}\n", indent, mname.bright_magenta()));
294 indent.add();
295
296 let parameter_types = &mtype.parameter_types;
297
298 let formatted = if !parameter_types.is_empty() {
299 format_type_list(¶meter_types[1..]).truecolor(180, 180, 180)
300 } else {
301 format_type_list(&[]).truecolor(180, 180, 180)
302 };
303
304 output.push_str(&format!(
305 "{} ↳ {}: {}\n",
306 indent,
307 "Args".blue(),
308 formatted
309 ));
310
311 output.push_str(&format!(
312 "{} ↳ {}: {}\n",
313 indent,
314 "Returns".blue(),
315 format_return_type(&mtype.return_type).truecolor(180, 180, 180)
316 ));
317
318 indent.remove();
319 }
320 }
321 }
322 }
323 }
324
325 println!("{output}");
326}
327
328fn format_type_list(types: &[InferredType]) -> String {
329 if types.is_empty() {
330 "()".to_string()
331 } else {
332 types
333 .iter()
334 .map(|t| wasm_wave::wasm::DisplayType(&AnalysedType::try_from(t).unwrap()).to_string())
335 .collect::<Vec<_>>()
336 .join(", ")
337 }
338}
339
340fn format_return_type(typ: &Option<InferredType>) -> String {
341 typ.as_ref()
342 .map(|t| wasm_wave::wasm::DisplayType(&AnalysedType::try_from(t).unwrap()).to_string())
343 .unwrap_or_else(|| "()".to_string())
344}
345
346#[derive(Default)]
347struct HierarchyNode<'a> {
348 functions: Vec<(String, &'a FunctionType)>,
349 resources: BTreeMap<String, ResourceNode<'a>>,
350}
351
352#[derive(Default)]
353struct ResourceNode<'a> {
354 constructor: Option<&'a FunctionType>,
355 methods: Vec<(String, &'a FunctionType)>,
356}
357
358fn print_rib_compilation_error(error: &RibCompilationError) {
359 match error {
360 RibCompilationError::RibStaticAnalysisError(msg) => {
361 println!("{} {}", "[rib static analysis error]".red(), msg.red());
362 }
363
364 RibCompilationError::UnsupportedGlobalInput {
365 invalid_global_inputs: found,
366 valid_global_inputs: expected,
367 } => {
368 println!(
369 "{} {} {}",
370 "[unsupported input]".red(),
371 "found:".yellow(),
372 found.join(", ").white()
373 );
374 println!(
375 "{} {} {}",
376 "[supported inputs]".green(),
377 "expected:".yellow(),
378 expected.join(", ").white()
379 );
380 }
381 RibCompilationError::RibTypeError(compilation_error) => {
382 let cause = &compilation_error.cause;
383 let position = &compilation_error.source_span;
384
385 println!("{}", "[compilation error]".red().bold());
386 println!("{} {}", "[position]".yellow(), position.start_column());
387 println!(
388 "{} {}",
389 "[expression]".yellow(),
390 compilation_error
391 .expr
392 .as_ref()
393 .map(|x| x.to_string())
394 .unwrap_or_default()
395 .white()
396 );
397 println!("{} {}", "[cause]".yellow(), cause.bright_red().bold());
398
399 if !compilation_error.additional_error_details.is_empty() {
400 for detail in &compilation_error.additional_error_details {
401 println!("{} {}", "[help]".yellow(), detail.cyan());
402 }
403 }
404
405 if !compilation_error.help_messages.is_empty() {
406 for message in &compilation_error.help_messages {
407 println!("{} {}", "[help]".yellow(), message.cyan());
408 }
409 }
410 }
411 RibCompilationError::InvalidSyntax(script) => {
412 println!("{} {}", "[invalid script]".red(), script.white());
413 }
414 RibCompilationError::ByteCodeGenerationFail(error) => {
415 println!(
416 "{} {}",
417 "[internal bytecode generation error]".red(),
418 error.to_string().red()
419 );
420 }
421 }
422}
423
424fn print_bootstrap_error(error: &ReplBootstrapError) {
425 match error {
426 ReplBootstrapError::ReplHistoryFileError(msg) => {
427 println!("{} {}", "[warn]".yellow(), msg);
428 }
429 ReplBootstrapError::ComponentLoadError(msg) => {
430 println!("{} {}", "[error]".red(), msg);
431 }
432 ReplBootstrapError::MultipleComponentsFound(msg) => {
433 println!("{} {}", "[error]".red(), msg);
434 println!(
435 "{}",
436 "specify the component name when bootstrapping repl".yellow()
437 );
438 }
439 ReplBootstrapError::NoComponentsFound => {
440 println!(
441 "{} no components found in the repl context",
442 "[warn]".yellow()
443 );
444 }
445 }
446}
447
448fn display_for_resource_handle(type_handle: &TypeHandle) -> String {
450 let resource_id = &type_handle.resource_id.0;
451 let uri = &type_handle.mode;
452
453 let mode = match uri {
454 AnalysedResourceMode::Owned => "owned",
455 AnalysedResourceMode::Borrowed => "borrowed",
456 };
457
458 format!("handle<resource-id:{}, mode:{}>", resource_id, mode)
459}
460
461pub struct Indent {
462 level: usize,
463}
464
465impl Default for Indent {
466 fn default() -> Self {
467 Self::new()
468 }
469}
470
471impl Indent {
472 pub fn new() -> Self {
473 Self { level: 0 }
474 }
475
476 pub fn add(&mut self) {
477 self.level += 2;
478 }
479
480 pub fn remove(&mut self) {
481 if self.level >= 2 {
482 self.level -= 2;
483 }
484 }
485}
486
487impl Display for Indent {
488 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
489 write!(f, "{}", " ".repeat(self.level))?;
490 Ok(())
491 }
492}