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    /// Invalid value error.
85    #[error("invalid value: {0}")]
86    InvalidValue(String),
87
88    /// Serialization error.
89    #[error("serialization error: {0}")]
90    Serialization(String),
91}
92
93impl Error {
94    /// Create a not permitted error.
95    pub fn not_permitted(msg: impl Into<String>) -> Self {
96        Self::NotPermitted(msg.into())
97    }
98
99    /// Create a path not allowed error.
100    pub fn path_not_allowed(path: impl Into<String>) -> Self {
101        Self::PathNotAllowed(path.into())
102    }
103
104    /// Create a host not allowed error.
105    pub fn host_not_allowed(host: impl Into<String>) -> Self {
106        Self::HostNotAllowed(host.into())
107    }
108
109    /// Create a timeout error.
110    pub fn timeout(duration: std::time::Duration) -> Self {
111        Self::Timeout(duration)
112    }
113
114    /// Create a process error.
115    pub fn process(msg: impl Into<String>) -> Self {
116        Self::Process(msg.into())
117    }
118
119    /// Create a process exit error.
120    pub fn process_exit(code: i32, message: impl Into<String>) -> Self {
121        Self::ProcessExit {
122            code,
123            message: message.into(),
124        }
125    }
126
127    /// Create a filesystem error.
128    pub fn filesystem(msg: impl Into<String>) -> Self {
129        Self::Filesystem(msg.into())
130    }
131
132    /// Create a network error.
133    pub fn network(msg: impl Into<String>) -> Self {
134        Self::Network(msg.into())
135    }
136
137    /// Create a format error.
138    pub fn format(msg: impl Into<String>) -> Self {
139        Self::Format(msg.into())
140    }
141
142    /// Create an invalid argument error.
143    pub fn invalid_argument(msg: impl Into<String>) -> Self {
144        Self::InvalidArgument(msg.into())
145    }
146
147    /// Check if this is a safety-related error.
148    pub fn is_safety_error(&self) -> bool {
149        matches!(
150            self,
151            Self::NotPermitted(_) | Self::PathNotAllowed(_) | Self::HostNotAllowed(_)
152        )
153    }
154
155    /// Check if this is a timeout error.
156    pub fn is_timeout(&self) -> bool {
157        matches!(self, Self::Timeout(_))
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn test_error_display() {
167        let err = Error::path_not_allowed("/etc/passwd");
168        assert!(err.to_string().contains("/etc/passwd"));
169
170        let err = Error::process_exit(1, "command failed");
171        assert!(err.to_string().contains("code 1"));
172    }
173
174    #[test]
175    fn test_error_classification() {
176        assert!(Error::not_permitted("test").is_safety_error());
177        assert!(Error::path_not_allowed("/tmp").is_safety_error());
178        assert!(!Error::process("test").is_safety_error());
179
180        assert!(Error::timeout(std::time::Duration::from_secs(1)).is_timeout());
181        assert!(!Error::process("test").is_timeout());
182    }
183}