Skip to main content

skiff_cli/
error.rs

1//! Error types and process exit codes.
2//!
3//! - Exit `2` — usage / bad args ([`Error::Usage`])
4//! - Exit `1` — runtime / tool / I/O failure
5
6use std::fmt;
7
8use thiserror::Error;
9
10/// Process exit code for CLI failures.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ExitCode {
13    Runtime = 1,
14    Usage = 2,
15}
16
17impl From<ExitCode> for i32 {
18    fn from(value: ExitCode) -> Self {
19        value as i32
20    }
21}
22
23#[derive(Debug, Error)]
24pub enum Error {
25    #[error("{0}")]
26    Usage(String),
27
28    #[error("{0}")]
29    Runtime(String),
30
31    #[error(transparent)]
32    Io(#[from] std::io::Error),
33
34    #[error(transparent)]
35    Json(#[from] serde_json::Error),
36
37    #[error(transparent)]
38    Yaml(#[from] serde_yaml::Error),
39}
40
41impl Error {
42    pub fn usage(msg: impl Into<String>) -> Self {
43        Self::Usage(msg.into())
44    }
45
46    pub fn runtime(msg: impl Into<String>) -> Self {
47        Self::Runtime(msg.into())
48    }
49
50    pub fn exit_code(&self) -> i32 {
51        match self {
52            Self::Usage(_) => ExitCode::Usage.into(),
53            _ => ExitCode::Runtime.into(),
54        }
55    }
56}
57
58/// Display with anyhow-style `:#` context via Debug for nested sources.
59pub struct Report<'a>(pub &'a Error);
60
61impl fmt::Display for Report<'_> {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        write!(f, "{}", self.0)
64    }
65}
66
67pub type Result<T> = std::result::Result<T, Error>;