use crate::errors::{CfgdError, ConfigError};
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExitCode {
Success = 0,
Error = 1,
UpdateAvailable = 2,
NoConfig = 3,
ConfigInvalid = 4,
DriftDetected = 5,
NotFound = 6,
}
impl ExitCode {
pub const fn as_i32(self) -> i32 {
self as i32
}
pub fn exit(self) -> ! {
std::process::exit(self.as_i32())
}
}
impl From<ExitCode> for i32 {
fn from(code: ExitCode) -> i32 {
code as i32
}
}
pub fn exit_code_for_error(err: &CfgdError) -> ExitCode {
match err {
CfgdError::Config(ConfigError::NotFound { .. }) => ExitCode::NoConfig,
CfgdError::Config(_) => ExitCode::ConfigInvalid,
_ => ExitCode::Error,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn distinct_integer_codes() {
let codes = [
ExitCode::Success.as_i32(),
ExitCode::Error.as_i32(),
ExitCode::UpdateAvailable.as_i32(),
ExitCode::NoConfig.as_i32(),
ExitCode::ConfigInvalid.as_i32(),
ExitCode::DriftDetected.as_i32(),
ExitCode::NotFound.as_i32(),
];
let mut seen = std::collections::HashSet::new();
for c in codes {
assert!(seen.insert(c), "duplicate exit code {}", c);
}
}
#[test]
fn stable_wire_values() {
assert_eq!(ExitCode::Success.as_i32(), 0);
assert_eq!(ExitCode::Error.as_i32(), 1);
assert_eq!(ExitCode::UpdateAvailable.as_i32(), 2);
assert_eq!(ExitCode::NoConfig.as_i32(), 3);
assert_eq!(ExitCode::ConfigInvalid.as_i32(), 4);
assert_eq!(ExitCode::DriftDetected.as_i32(), 5);
assert_eq!(ExitCode::NotFound.as_i32(), 6);
}
#[test]
fn config_not_found_maps_to_no_config() {
let err = CfgdError::Config(ConfigError::NotFound {
path: PathBuf::from("/nonexistent/cfgd.yaml"),
});
assert_eq!(exit_code_for_error(&err), ExitCode::NoConfig);
}
#[test]
fn config_invalid_maps_to_config_invalid() {
let err = CfgdError::Config(ConfigError::Invalid {
message: "missing apiVersion".into(),
});
assert_eq!(exit_code_for_error(&err), ExitCode::ConfigInvalid);
let err = CfgdError::Config(ConfigError::ProfileNotFound { name: "dev".into() });
assert_eq!(exit_code_for_error(&err), ExitCode::ConfigInvalid);
}
#[test]
fn non_config_error_maps_to_generic() {
let err = CfgdError::Io(std::io::Error::other("boom"));
assert_eq!(exit_code_for_error(&err), ExitCode::Error);
}
#[test]
fn i32_conversion_matches_as_i32() {
let code: i32 = ExitCode::DriftDetected.into();
assert_eq!(code, ExitCode::DriftDetected.as_i32());
}
}