terraphim_agent/robot/
exit_codes.rs

1//! Exit codes for robot mode
2//!
3//! Standard exit codes for machine consumption, following Unix conventions
4//! with domain-specific extensions.
5
6use std::process::Termination;
7
8/// Exit codes for terraphim-agent robot mode
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[repr(u8)]
11pub enum ExitCode {
12    /// Operation completed successfully
13    Success = 0,
14    /// General/unspecified error
15    ErrorGeneral = 1,
16    /// Invalid arguments or syntax error
17    ErrorUsage = 2,
18    /// Required index not initialized
19    ErrorIndexMissing = 3,
20    /// No results found for query
21    ErrorNotFound = 4,
22    /// Authentication required or failed
23    ErrorAuth = 5,
24    /// Network or connectivity issue
25    ErrorNetwork = 6,
26    /// Operation timed out
27    ErrorTimeout = 7,
28}
29
30impl ExitCode {
31    /// Get the numeric exit code value
32    pub fn code(self) -> u8 {
33        self as u8
34    }
35
36    /// Get a human-readable description
37    pub fn description(self) -> &'static str {
38        match self {
39            ExitCode::Success => "Operation completed successfully",
40            ExitCode::ErrorGeneral => "General error",
41            ExitCode::ErrorUsage => "Invalid arguments or syntax",
42            ExitCode::ErrorIndexMissing => "Required index not initialized",
43            ExitCode::ErrorNotFound => "No results found",
44            ExitCode::ErrorAuth => "Authentication required",
45            ExitCode::ErrorNetwork => "Network error",
46            ExitCode::ErrorTimeout => "Operation timed out",
47        }
48    }
49
50    /// Get the exit code name for JSON output
51    pub fn name(self) -> &'static str {
52        match self {
53            ExitCode::Success => "SUCCESS",
54            ExitCode::ErrorGeneral => "ERROR_GENERAL",
55            ExitCode::ErrorUsage => "ERROR_USAGE",
56            ExitCode::ErrorIndexMissing => "ERROR_INDEX_MISSING",
57            ExitCode::ErrorNotFound => "ERROR_NOT_FOUND",
58            ExitCode::ErrorAuth => "ERROR_AUTH",
59            ExitCode::ErrorNetwork => "ERROR_NETWORK",
60            ExitCode::ErrorTimeout => "ERROR_TIMEOUT",
61        }
62    }
63
64    /// Convert from u8
65    pub fn from_code(code: u8) -> Self {
66        match code {
67            0 => ExitCode::Success,
68            2 => ExitCode::ErrorUsage,
69            3 => ExitCode::ErrorIndexMissing,
70            4 => ExitCode::ErrorNotFound,
71            5 => ExitCode::ErrorAuth,
72            6 => ExitCode::ErrorNetwork,
73            7 => ExitCode::ErrorTimeout,
74            _ => ExitCode::ErrorGeneral,
75        }
76    }
77}
78
79impl From<ExitCode> for std::process::ExitCode {
80    fn from(code: ExitCode) -> Self {
81        std::process::ExitCode::from(code.code())
82    }
83}
84
85impl Termination for ExitCode {
86    fn report(self) -> std::process::ExitCode {
87        self.into()
88    }
89}
90
91impl std::fmt::Display for ExitCode {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        write!(f, "{} ({})", self.name(), self.code())
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_exit_code_values() {
103        assert_eq!(ExitCode::Success.code(), 0);
104        assert_eq!(ExitCode::ErrorGeneral.code(), 1);
105        assert_eq!(ExitCode::ErrorUsage.code(), 2);
106        assert_eq!(ExitCode::ErrorIndexMissing.code(), 3);
107        assert_eq!(ExitCode::ErrorNotFound.code(), 4);
108        assert_eq!(ExitCode::ErrorAuth.code(), 5);
109        assert_eq!(ExitCode::ErrorNetwork.code(), 6);
110        assert_eq!(ExitCode::ErrorTimeout.code(), 7);
111    }
112
113    #[test]
114    fn test_exit_code_from_code() {
115        assert_eq!(ExitCode::from_code(0), ExitCode::Success);
116        assert_eq!(ExitCode::from_code(2), ExitCode::ErrorUsage);
117        assert_eq!(ExitCode::from_code(99), ExitCode::ErrorGeneral); // Unknown maps to general
118    }
119}