Skip to main content

torvyn_cli/errors/
diagnostic.rs

1//! Diagnostic rendering for CLI errors.
2//!
3//! Renders errors in the four-part format mandated by Doc 07 §5.1:
4//! what went wrong, where, why, how to fix.
5
6use crate::errors::CliError;
7use crate::output::OutputContext;
8
9/// Render a [`CliError`] to stderr with styled diagnostics.
10///
11/// COLD PATH — called at most once per invocation.
12pub fn render_cli_error(ctx: &OutputContext, err: &CliError) {
13    match err {
14        CliError::Config {
15            detail,
16            file,
17            suggestion,
18        } => {
19            print_error_header(ctx, detail);
20            if let Some(f) = file {
21                print_location(ctx, f);
22            }
23            print_help(ctx, suggestion);
24        }
25        CliError::Contract {
26            detail,
27            diagnostics,
28        } => {
29            print_error_header(ctx, detail);
30            for d in diagnostics {
31                eprintln!("  {d}");
32            }
33        }
34        CliError::Link {
35            detail,
36            diagnostics,
37        } => {
38            print_error_header(ctx, detail);
39            for d in diagnostics {
40                eprintln!("  {d}");
41            }
42        }
43        CliError::Runtime { detail, context } => {
44            print_error_header(ctx, detail);
45            if let Some(c) = context {
46                eprintln!("  context: {c}");
47            }
48        }
49        CliError::Packaging { detail, suggestion } => {
50            print_error_header(ctx, detail);
51            print_help(ctx, suggestion);
52        }
53        CliError::Security { detail, suggestion } => {
54            print_error_header(ctx, detail);
55            print_help(ctx, suggestion);
56        }
57        CliError::Environment { detail, fix } => {
58            print_error_header(ctx, detail);
59            print_help(ctx, &format!("fix: {fix}"));
60        }
61        CliError::Io { detail, path } => {
62            print_error_header(ctx, detail);
63            if let Some(p) = path {
64                print_location(ctx, p);
65            }
66        }
67        CliError::Internal { detail } => {
68            print_error_header(ctx, &format!("Internal error: {detail}"));
69            print_help(
70                ctx,
71                "This is likely a bug. Please report it at https://github.com/torvyn/torvyn/issues",
72            );
73        }
74        CliError::NotImplemented { command } => {
75            print_error_header(ctx, &format!("Command '{command}' is not yet implemented"));
76            print_help(
77                ctx,
78                "This command will be available in a future release (Part B).",
79            );
80        }
81    }
82}
83
84/// Print the error header line: `error: <message>`
85fn print_error_header(ctx: &OutputContext, message: &str) {
86    if ctx.color_enabled {
87        eprintln!("\n{} {message}\n", console::style("error:").red().bold());
88    } else {
89        eprintln!("\nerror: {message}\n");
90    }
91}
92
93/// Print a file location line.
94fn print_location(ctx: &OutputContext, location: &str) {
95    if ctx.color_enabled {
96        eprintln!("  {} {location}", console::style("-->").dim());
97    } else {
98        eprintln!("  --> {location}");
99    }
100}
101
102/// Print a help/suggestion line.
103fn print_help(ctx: &OutputContext, help: &str) {
104    if ctx.color_enabled {
105        eprintln!("  {} {help}", console::style("help:").cyan().bold());
106    } else {
107        eprintln!("  help: {help}");
108    }
109}