fusabi_stdlib_ext/
error.rs

1//! Error types for stdlib-ext operations.
2
3use thiserror::Error;
4
5/// Result type alias using [`Error`].
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// Errors that can occur during stdlib operations.
9#[derive(Error, Debug)]
10pub enum Error {
11    /// Operation not permitted by safety config.
12    #[error("operation not permitted: {0}")]
13    NotPermitted(String),
14
15    /// Path not in allowlist.
16    #[error("path not allowed: {0}")]
17    PathNotAllowed(String),
18
19    /// Host not in allowlist.
20    #[error("host not allowed: {0}")]
21    HostNotAllowed(String),
22
23    /// Operation timed out.
24    #[error("operation timed out after {0:?}")]
25    Timeout(std::time::Duration),
26
27    /// Process execution failed.
28    #[error("process error: {0}")]
29    Process(String),
30
31    /// Process exit with non-zero code.
32    #[error("process exited with code {code}: {message}")]
33    ProcessExit {
34        /// Exit code.
35        code: i32,
36        /// Error message.
37        message: String,
38    },
39
40    /// Filesystem error.
41    #[error("filesystem error: {0}")]
42    Filesystem(String),
43
44    /// Network error.
45    #[error("network error: {0}")]
46    Network(String),
47
48    /// Format error.
49    #[error("format error: {0}")]
50    Format(String),
51
52    /// Environment error.
53    #[error("environment error: {0}")]
54    Environment(String),
55
56    /// Module not available.
57    #[error("module not available: {0}")]
58    ModuleNotAvailable(String),
59
60    /// Invalid argument.
61    #[error("invalid argument: {0}")]
62    InvalidArgument(String),
63
64    /// IO error.
65    #[error("io error: {0}")]
66    Io(#[from] std::io::Error),
67
68    /// Host error.
69    #[error("host error: {0}")]
70    Host(#[from] fusabi_host::Error),
71
72    /// Internal error.
73    #[error("internal error: {0}")]
74    Internal(String),
75
76    /// Terminal UI error.
77    #[error("terminal UI error: {0}")]
78    TerminalUI(String),
79
80    /// Kubernetes error.
81    #[error("kubernetes error: {0}")]
82    K8s(String),
83}
84
85impl Error {
86    /// Create a not permitted error.
87    pub fn not_permitted(msg: impl Into<String>) -> Self {
88        Self::NotPermitted(msg.into())
89    }
90
91    /// Create a path not allowed error.
92    pub fn path_not_allowed(path: impl Into<String>) -> Self {
93        Self::PathNotAllowed(path.into())
94    }
95
96    /// Create a host not allowed error.
97    pub fn host_not_allowed(host: impl Into<String>) -> Self {
98        Self::HostNotAllowed(host.into())
99    }
100
101    /// Create a timeout error.
102    pub fn timeout(duration: std::time::Duration) -> Self {
103        Self::Timeout(duration)
104    }
105
106    /// Create a process error.
107    pub fn process(msg: impl Into<String>) -> Self {
108        Self::Process(msg.into())
109    }
110
111    /// Create a process exit error.
112    pub fn process_exit(code: i32, message: impl Into<String>) -> Self {
113        Self::ProcessExit {
114            code,
115            message: message.into(),
116        }
117    }
118
119    /// Create a filesystem error.
120    pub fn filesystem(msg: impl Into<String>) -> Self {
121        Self::Filesystem(msg.into())
122    }
123
124    /// Create a network error.
125    pub fn network(msg: impl Into<String>) -> Self {
126        Self::Network(msg.into())
127    }
128
129    /// Create a format error.
130    pub fn format(msg: impl Into<String>) -> Self {
131        Self::Format(msg.into())
132    }
133
134    /// Create an invalid argument error.
135    pub fn invalid_argument(msg: impl Into<String>) -> Self {
136        Self::InvalidArgument(msg.into())
137    }
138
139    /// Check if this is a safety-related error.
140    pub fn is_safety_error(&self) -> bool {
141        matches!(
142            self,
143            Self::NotPermitted(_) | Self::PathNotAllowed(_) | Self::HostNotAllowed(_)
144        )
145    }
146
147    /// Check if this is a timeout error.
148    pub fn is_timeout(&self) -> bool {
149        matches!(self, Self::Timeout(_))
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn test_error_display() {
159        let err = Error::path_not_allowed("/etc/passwd");
160        assert!(err.to_string().contains("/etc/passwd"));
161
162        let err = Error::process_exit(1, "command failed");
163        assert!(err.to_string().contains("code 1"));
164    }
165
166    #[test]
167    fn test_error_classification() {
168        assert!(Error::not_permitted("test").is_safety_error());
169        assert!(Error::path_not_allowed("/tmp").is_safety_error());
170        assert!(!Error::process("test").is_safety_error());
171
172        assert!(Error::timeout(std::time::Duration::from_secs(1)).is_timeout());
173        assert!(!Error::process("test").is_timeout());
174    }
175}