use std::{io, result};
use thiserror::Error;
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum Error {
#[error("IO error: {kind} - {message}")]
Io { kind: io::ErrorKind, message: String },
#[error("IOKit error: {0}")]
IOKit(String),
#[error("Temperature monitoring error: {0}")]
Temperature(String),
#[error("CPU monitoring error: {0}")]
Cpu(String),
#[error("GPU monitoring error: {0}")]
Gpu(String),
#[error("Memory monitoring error: {0}")]
Memory(String),
#[error("Network monitoring error: {0}")]
Network(String),
#[error("Process monitoring error: {0}")]
Process(String),
#[error("System info error: {0}")]
SystemInfo(String),
#[error("Service not found: {0}")]
ServiceNotFound(String),
#[error("Invalid data: {0}")]
InvalidData(String),
#[error("Not implemented: {0}")]
NotImplemented(String),
#[error("Feature not available: {0}")]
NotAvailable(String),
#[error("Permission denied: {0}")]
PermissionDenied(String),
#[error("System error: {0}")]
System(String),
#[error("Error: {0}")]
Other(String),
}
impl Error {
pub fn io_kit<S: Into<String>>(message: S) -> Self {
Error::IOKit(message.into())
}
pub fn system<S: Into<String>>(message: S) -> Self {
Error::System(message.into())
}
pub fn invalid_data<S: Into<String>>(message: S) -> Self {
Error::InvalidData(message.into())
}
pub fn not_implemented<S: Into<String>>(feature: S) -> Self {
Error::NotImplemented(feature.into())
}
pub fn not_available<S: Into<String>>(feature: S) -> Self {
Error::NotAvailable(feature.into())
}
pub fn permission_denied<S: Into<String>>(message: S) -> Self {
Error::PermissionDenied(message.into())
}
pub fn service_not_found<S: Into<String>>(service: S) -> Self {
Error::ServiceNotFound(service.into())
}
pub fn process_error<S: Into<String>>(message: S) -> Self {
Error::Process(message.into())
}
pub fn details(&self) -> String {
match self {
Error::Io { kind, message } => format!("{}: {}", message, kind),
Error::PermissionDenied(msg) => {
format!("Permission denied: {}. Try running with elevated privileges.", msg)
},
Error::IOKit(msg) => format!(
"IOKit error: {}. This might be a compatibility issue with your macOS version.",
msg
),
Error::NotAvailable(msg) => format!(
"Feature not available: {}. This feature might not be supported on your hardware.",
msg
),
_ => format!("{}", self),
}
}
pub fn is_permission_error(&self) -> bool {
matches!(self, Error::PermissionDenied(_))
|| matches!(self, Error::Io { kind, .. } if *kind == io::ErrorKind::PermissionDenied)
}
pub fn is_not_available(&self) -> bool {
matches!(self, Error::NotAvailable(_))
}
}
pub type Result<T> = result::Result<T, Error>;
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io { kind: err.kind(), message: err.to_string() }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Error as IoError, ErrorKind};
#[test]
fn test_error_creation_methods() {
let e1 = Error::io_kit("test io_kit error");
assert!(matches!(e1, Error::IOKit(s) if s == "test io_kit error"));
let e2 = Error::system("test system error");
assert!(matches!(e2, Error::System(s) if s == "test system error"));
let e3 = Error::invalid_data("test invalid data");
assert!(matches!(e3, Error::InvalidData(s) if s == "test invalid data"));
let e4 = Error::not_implemented("test feature");
assert!(matches!(e4, Error::NotImplemented(s) if s == "test feature"));
let e5 = Error::not_available("test feature");
assert!(matches!(e5, Error::NotAvailable(s) if s == "test feature"));
let e6 = Error::permission_denied("test permission");
assert!(matches!(e6, Error::PermissionDenied(s) if s == "test permission"));
let e7 = Error::service_not_found("test service");
assert!(matches!(e7, Error::ServiceNotFound(s) if s == "test service"));
let e8 = Error::process_error("test process error");
assert!(matches!(e8, Error::Process(s) if s == "test process error"));
}
#[test]
fn test_error_details_io() {
let e1 = Error::Io { kind: ErrorKind::NotFound, message: "file not found".to_string() };
let details = e1.details();
assert!(details.contains("file not found"));
assert!(details.contains("not found"));
}
#[test]
fn test_error_details_permission() {
let e = Error::PermissionDenied("access denied".to_string());
assert!(e.details().contains("Permission denied: access denied"));
assert!(e.details().contains("elevated privileges"));
}
#[test]
fn test_error_details_iokit() {
let e = Error::IOKit("service failed".to_string());
assert!(e.details().contains("IOKit error: service failed"));
}
#[test]
fn test_error_details_not_available() {
let e = Error::NotAvailable("GPU features".to_string());
assert!(e.details().contains("Feature not available: GPU features"));
}
#[test]
fn test_error_details_other() {
let e = Error::Other("generic error".to_string());
assert_eq!(e.details(), "Error: generic error");
}
#[test]
fn test_error_is_permission_error() {
let e1 = Error::PermissionDenied("test".to_string());
assert!(e1.is_permission_error());
let e2 = Error::Io { kind: ErrorKind::PermissionDenied, message: "test".to_string() };
assert!(e2.is_permission_error());
let e3 = Error::Io { kind: ErrorKind::NotFound, message: "test".to_string() };
assert!(!e3.is_permission_error());
let e4 = Error::Other("test".to_string());
assert!(!e4.is_permission_error());
}
#[test]
fn test_error_is_not_available() {
let e1 = Error::NotAvailable("test".to_string());
assert!(e1.is_not_available());
let e2 = Error::NotImplemented("test".to_string());
assert!(!e2.is_not_available());
}
#[test]
fn test_from_io_error() {
let io_err = IoError::new(ErrorKind::ConnectionRefused, "connection error");
let err: Error = io_err.into();
if let Error::Io { kind, message } = err {
assert_eq!(kind, ErrorKind::ConnectionRefused);
assert!(message.contains("connection error"));
} else {
panic!("Error was not converted to Error::Io variant");
}
}
}