Skip to main content

via/
error.rs

1use std::io;
2use std::path::PathBuf;
3
4#[derive(Debug, thiserror::Error)]
5pub enum ViaError {
6    #[error("{0}")]
7    InvalidCli(String),
8
9    #[error(transparent)]
10    Clap(#[from] clap::Error),
11
12    #[error("could not determine config path: {0}")]
13    ConfigNotFound(String),
14
15    #[error("failed to read config `{path}`: {source}")]
16    ReadConfig { path: PathBuf, source: io::Error },
17
18    #[error("failed to parse config: {0}")]
19    ParseConfig(#[from] toml::de::Error),
20
21    #[error("invalid config: {0}")]
22    InvalidConfig(String),
23
24    #[error("unknown service `{0}`")]
25    UnknownService(String),
26
27    #[error("unknown capability `{capability}` for service `{service}`")]
28    UnknownCapability { service: String, capability: String },
29
30    #[error("missing required argument: {0}")]
31    MissingArgument(String),
32
33    #[error("invalid argument: {0}")]
34    InvalidArgument(String),
35
36    #[error("program `{program}` was not found: {source}")]
37    MissingProgram { program: String, source: io::Error },
38
39    #[error("program `{program}` failed with status {status:?}: {stderr}")]
40    ExternalCommandFailed {
41        program: String,
42        status: Option<i32>,
43        stderr: String,
44    },
45
46    #[error("secret `{secret}` is not configured for service `{service}`")]
47    UnknownSecret { service: String, secret: String },
48
49    #[error("doctor checks failed; see output above for setup guidance")]
50    DoctorFailed,
51
52    #[error("http error: {0}")]
53    Http(#[from] reqwest::Error),
54
55    #[error("json error: {0}")]
56    Json(#[from] serde_json::Error),
57
58    #[error("io error: {0}")]
59    Io(#[from] io::Error),
60}
61
62impl ViaError {
63    pub fn exit_code(&self) -> u8 {
64        match self {
65            ViaError::Clap(error) => {
66                if error.use_stderr() {
67                    2
68                } else {
69                    0
70                }
71            }
72            ViaError::InvalidCli(_)
73            | ViaError::InvalidConfig(_)
74            | ViaError::UnknownService(_)
75            | ViaError::UnknownCapability { .. }
76            | ViaError::MissingArgument(_)
77            | ViaError::InvalidArgument(_)
78            | ViaError::UnknownSecret { .. } => 2,
79            ViaError::ConfigNotFound(_)
80            | ViaError::ReadConfig { .. }
81            | ViaError::ParseConfig(_)
82            | ViaError::MissingProgram { .. }
83            | ViaError::ExternalCommandFailed { .. }
84            | ViaError::DoctorFailed
85            | ViaError::Http(_)
86            | ViaError::Json(_)
87            | ViaError::Io(_) => 1,
88        }
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn config_and_usage_errors_exit_two() {
98        assert_eq!(ViaError::InvalidConfig("bad".to_owned()).exit_code(), 2);
99        assert_eq!(ViaError::UnknownService("github".to_owned()).exit_code(), 2);
100    }
101
102    #[test]
103    fn runtime_errors_exit_one() {
104        let error = ViaError::ConfigNotFound("missing".to_owned());
105
106        assert_eq!(error.exit_code(), 1);
107    }
108}