specman_cli/
error.rs

1use std::fmt;
2use std::process::ExitCode;
3
4use clap::error::ErrorKind as ClapErrorKind;
5use specman::SpecmanError;
6
7const EX_OK: u8 = 0;
8const EX_USAGE: u8 = 64;
9const EX_DATAERR: u8 = 65;
10const EX_SOFTWARE: u8 = 70;
11const EX_OSERR: u8 = 71;
12const EX_CONFIG: u8 = 78;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum ExitStatus {
16    Ok,
17    Usage,
18    Data,
19    Io,
20    Config,
21    Software,
22}
23
24impl ExitStatus {
25    pub fn code(self) -> u8 {
26        match self {
27            ExitStatus::Ok => EX_OK,
28            ExitStatus::Usage => EX_USAGE,
29            ExitStatus::Data => EX_DATAERR,
30            ExitStatus::Io => EX_OSERR,
31            ExitStatus::Config => EX_CONFIG,
32            ExitStatus::Software => EX_SOFTWARE,
33        }
34    }
35}
36
37#[derive(Debug)]
38pub struct CliError {
39    message: String,
40    status: ExitStatus,
41}
42
43impl CliError {
44    pub fn new(message: impl Into<String>, status: ExitStatus) -> Self {
45        Self {
46            message: message.into(),
47            status,
48        }
49    }
50
51    pub fn exit_code(&self) -> ExitCode {
52        ExitCode::from(self.status.code())
53    }
54
55    pub fn print(&self) {
56        if !self.message.is_empty() {
57            eprintln!("{}", self.message);
58        }
59    }
60}
61
62impl From<SpecmanError> for CliError {
63    fn from(err: SpecmanError) -> Self {
64        let status = match err {
65            SpecmanError::Template(_) | SpecmanError::Dependency(_) => ExitStatus::Data,
66            SpecmanError::Workspace(_) => ExitStatus::Usage,
67            SpecmanError::Serialization(_) => ExitStatus::Software,
68            SpecmanError::Io(_) => ExitStatus::Io,
69        };
70        CliError::new(err.to_string(), status)
71    }
72}
73
74impl From<clap::Error> for CliError {
75    fn from(err: clap::Error) -> Self {
76        let status = match err.kind() {
77            ClapErrorKind::DisplayHelp | ClapErrorKind::DisplayVersion => ExitStatus::Ok,
78            _ => ExitStatus::Usage,
79        };
80        if status == ExitStatus::Ok {
81            let _ = err.print();
82            CliError::new(String::new(), status)
83        } else {
84            CliError::new(err.to_string(), status)
85        }
86    }
87}
88
89impl From<std::io::Error> for CliError {
90    fn from(err: std::io::Error) -> Self {
91        CliError::new(err.to_string(), ExitStatus::Io)
92    }
93}
94
95impl fmt::Display for CliError {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        write!(f, "{}", self.message)
98    }
99}
100
101impl std::error::Error for CliError {}