Skip to main content

prax_cli/
error.rs

1//! CLI error types and result alias.
2
3use miette::Diagnostic;
4use thiserror::Error;
5
6/// Result type alias for CLI operations
7pub type CliResult<T> = Result<T, CliError>;
8
9/// CLI error types
10#[derive(Error, Debug, Diagnostic)]
11pub enum CliError {
12    /// IO error
13    #[error("IO error: {0}")]
14    #[diagnostic(code(prax::io))]
15    Io(#[from] std::io::Error),
16
17    /// Configuration error
18    #[error("Configuration error: {0}")]
19    #[diagnostic(code(prax::config))]
20    Config(String),
21
22    /// Schema parsing error
23    #[error("Schema error: {0}")]
24    #[diagnostic(code(prax::schema))]
25    Schema(String),
26
27    /// Validation error
28    #[error("Validation error: {0}")]
29    #[diagnostic(code(prax::validation))]
30    Validation(String),
31
32    /// Migration error
33    #[error("Migration error: {0}")]
34    #[diagnostic(code(prax::migration))]
35    Migration(String),
36
37    /// Database error
38    #[error("Database error: {0}")]
39    #[diagnostic(code(prax::database))]
40    Database(String),
41
42    /// Command error
43    #[error("Command error: {0}")]
44    #[diagnostic(code(prax::command))]
45    Command(String),
46
47    /// Format error
48    #[error("Format error: {0}")]
49    #[diagnostic(code(prax::format))]
50    Format(String),
51
52    /// Code generation error
53    #[error("Codegen error: {0}")]
54    #[diagnostic(code(prax::codegen))]
55    Codegen(String),
56}
57
58impl From<toml::de::Error> for CliError {
59    fn from(err: toml::de::Error) -> Self {
60        CliError::Config(format!("Failed to parse TOML: {}", err))
61    }
62}
63
64impl From<toml::ser::Error> for CliError {
65    fn from(err: toml::ser::Error) -> Self {
66        CliError::Config(format!("Failed to serialize TOML: {}", err))
67    }
68}
69
70impl From<prax_schema::LoadError> for CliError {
71    fn from(e: prax_schema::LoadError) -> Self {
72        use prax_schema::SchemaError;
73        // Resolve SourceId references to file paths for human-readable output.
74        let resolved = render_schema_error(&e.error, &e.sources);
75        match &e.error {
76            SchemaError::ValidationFailed { .. } => CliError::Validation(resolved),
77            _ => CliError::Schema(resolved),
78        }
79    }
80}
81
82impl From<prax_schema::SchemaError> for CliError {
83    fn from(e: prax_schema::SchemaError) -> Self {
84        use prax_schema::SchemaError;
85        match &e {
86            SchemaError::ValidationFailed { .. } => CliError::Validation(e.to_string()),
87            _ => CliError::Schema(e.to_string()),
88        }
89    }
90}
91
92/// Render a SchemaError with file paths resolved from the SourceMap.
93fn render_schema_error(err: &prax_schema::SchemaError, sources: &prax_schema::SourceMap) -> String {
94    use prax_schema::SchemaError;
95    use std::fmt::Write;
96
97    let mut out = err.to_string();
98    match err {
99        SchemaError::ParseInFile { source, inner } => {
100            if let Some(p) = sources.path_of(*source) {
101                let _ = write!(out, "\n  in: {}\n  detail: {}", p.display(), inner);
102            }
103        }
104        SchemaError::DuplicateAcrossFiles { first, second, .. }
105        | SchemaError::MultipleDatasource { first, second } => {
106            if let (Some(a), Some(b)) = (
107                sources.path_of(first.source),
108                sources.path_of(second.source),
109            ) {
110                let _ = write!(
111                    out,
112                    "\n  first:  {} (bytes {}..{})\n  second: {} (bytes {}..{})",
113                    a.display(),
114                    first.span.start,
115                    first.span.end,
116                    b.display(),
117                    second.span.start,
118                    second.span.end,
119                );
120            }
121        }
122        SchemaError::ValidationFailed { errors, .. } => {
123            for (i, e) in errors.iter().enumerate() {
124                let _ = write!(out, "\n  {}. {}", i + 1, render_schema_error(e, sources));
125            }
126        }
127        _ => {}
128    }
129    out
130}