use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("operation not permitted: {0}")]
NotPermitted(String),
#[error("path not allowed: {0}")]
PathNotAllowed(String),
#[error("host not allowed: {0}")]
HostNotAllowed(String),
#[error("operation timed out after {0:?}")]
Timeout(std::time::Duration),
#[error("process error: {0}")]
Process(String),
#[error("process exited with code {code}: {message}")]
ProcessExit {
code: i32,
message: String,
},
#[error("filesystem error: {0}")]
Filesystem(String),
#[error("network error: {0}")]
Network(String),
#[error("format error: {0}")]
Format(String),
#[error("environment error: {0}")]
Environment(String),
#[error("module not available: {0}")]
ModuleNotAvailable(String),
#[error("invalid argument: {0}")]
InvalidArgument(String),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("host error: {0}")]
Host(#[from] fusabi_host::Error),
#[error("internal error: {0}")]
Internal(String),
#[error("terminal UI error: {0}")]
TerminalUI(String),
#[error("kubernetes error: {0}")]
K8s(String),
#[error("invalid value: {0}")]
InvalidValue(String),
#[error("serialization error: {0}")]
Serialization(String),
}
impl Error {
pub fn not_permitted(msg: impl Into<String>) -> Self {
Self::NotPermitted(msg.into())
}
pub fn path_not_allowed(path: impl Into<String>) -> Self {
Self::PathNotAllowed(path.into())
}
pub fn host_not_allowed(host: impl Into<String>) -> Self {
Self::HostNotAllowed(host.into())
}
pub fn timeout(duration: std::time::Duration) -> Self {
Self::Timeout(duration)
}
pub fn process(msg: impl Into<String>) -> Self {
Self::Process(msg.into())
}
pub fn process_exit(code: i32, message: impl Into<String>) -> Self {
Self::ProcessExit {
code,
message: message.into(),
}
}
pub fn filesystem(msg: impl Into<String>) -> Self {
Self::Filesystem(msg.into())
}
pub fn network(msg: impl Into<String>) -> Self {
Self::Network(msg.into())
}
pub fn format(msg: impl Into<String>) -> Self {
Self::Format(msg.into())
}
pub fn invalid_argument(msg: impl Into<String>) -> Self {
Self::InvalidArgument(msg.into())
}
pub fn is_safety_error(&self) -> bool {
matches!(
self,
Self::NotPermitted(_) | Self::PathNotAllowed(_) | Self::HostNotAllowed(_)
)
}
pub fn is_timeout(&self) -> bool {
matches!(self, Self::Timeout(_))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = Error::path_not_allowed("/etc/passwd");
assert!(err.to_string().contains("/etc/passwd"));
let err = Error::process_exit(1, "command failed");
assert!(err.to_string().contains("code 1"));
}
#[test]
fn test_error_classification() {
assert!(Error::not_permitted("test").is_safety_error());
assert!(Error::path_not_allowed("/tmp").is_safety_error());
assert!(!Error::process("test").is_safety_error());
assert!(Error::timeout(std::time::Duration::from_secs(1)).is_timeout());
assert!(!Error::process("test").is_timeout());
}
}