Skip to main content

cgn_core/
error.rs

1//! Workspace-wide error type.
2
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum Error {
7    #[error("io: {0}")]
8    Io(#[from] std::io::Error),
9
10    #[error("config: {0}")]
11    Config(String),
12
13    #[error("toml: {0}")]
14    Toml(#[from] toml::de::Error),
15
16    #[error("json: {0}")]
17    Json(#[from] serde_json::Error),
18
19    #[error("tls: {0}")]
20    Tls(String),
21
22    #[error("transport: {0}")]
23    Transport(#[from] tonic::transport::Error),
24
25    #[error("rpc: {0}")]
26    Status(#[from] tonic::Status),
27
28    #[error("etcd: {0}")]
29    Etcd(String),
30
31    #[error("invalid argument: {0}")]
32    InvalidArgument(String),
33
34    #[error("not found: {0}")]
35    NotFound(String),
36
37    #[error("unavailable: {0}")]
38    Unavailable(String),
39
40    #[error("internal: {0}")]
41    Internal(String),
42
43    #[error(transparent)]
44    Other(#[from] anyhow::Error),
45}
46
47pub type Result<T, E = Error> = std::result::Result<T, E>;
48
49/// Map an `Error` to the standard process exit code documented in
50/// `docs/reference/exit-codes.md`. All Cognitora binaries call this
51/// from their `main()` so the matrix is enforced uniformly.
52pub fn exit_code(err: &Error) -> i32 {
53    match err {
54        Error::Config(_) => 3,
55        Error::Toml(_) => 3,
56        Error::InvalidArgument(_) => 2,
57        Error::Tls(_) => 5,
58        Error::Etcd(_) => 4,
59        Error::Unavailable(_) => 4,
60        Error::NotFound(_) => 4,
61        Error::Io(e) if e.kind() == std::io::ErrorKind::AddrInUse => 7,
62        Error::Io(_) => 8,
63        Error::Transport(_) => 4,
64        Error::Status(_) => 4,
65        Error::Json(_) => 3,
66        Error::Internal(_) | Error::Other(_) => 1,
67    }
68}
69
70impl From<Error> for tonic::Status {
71    fn from(e: Error) -> Self {
72        use tonic::Code;
73        let code = match &e {
74            Error::InvalidArgument(_) => Code::InvalidArgument,
75            Error::NotFound(_) => Code::NotFound,
76            Error::Unavailable(_) => Code::Unavailable,
77            Error::Status(s) => return s.clone(),
78            _ => Code::Internal,
79        };
80        tonic::Status::new(code, e.to_string())
81    }
82}