// file: kb-core/src/error.rs // version: 6 //! Error and result primitives shared by the workspace. /// Workspace-wide result alias. pub type Result = std::result::Result; /// Workspace-wide explicit error type. /// /// The project avoids generic catch-all error crates, so this enum centralizes /// the first stable error families used by core, configuration, logging, /// applications, RPC, stores and execution modules. #[derive(Clone, Debug, Eq, PartialEq)] pub enum Error { /// Configuration or profile validation error. Config(std::string::String), /// Filesystem or standard I/O error. Io(std::string::String), /// JSON serialization, deserialization or schema validation error. Json(std::string::String), /// Tracing initialization or logging runtime error. Tracing(std::string::String), /// Tauri application or WebView runtime error. Tauri(std::string::String), /// HTTP transport error. Http(std::string::String), /// WebSocket transport error. Ws(std::string::String), /// Database or storage backend error. Db(std::string::String), /// Invalid internal state error. InvalidState(std::string::String), /// Operation requested while a client or subsystem is not connected. NotConnected(std::string::String), /// Feature intentionally scheduled for a later version. NotImplemented(std::string::String), /// Stable custom error code used while domains are still being split. Custom { /// Stable custom error code. code: std::string::String, /// Human-readable custom error message. message: std::string::String, }, } impl crate::Error { /// Creates a custom error value with a stable code. pub fn new( code: impl std::convert::Into, message: impl std::convert::Into, ) -> Self { return Self::Custom { code: code.into(), message: message.into(), }; } /// Creates a configuration error value. pub fn config(message: impl std::convert::Into) -> Self { return Self::Config(message.into()); } /// Creates an I/O error value. pub fn io(message: impl std::convert::Into) -> Self { return Self::Io(message.into()); } /// Creates a JSON error value. pub fn json(message: impl std::convert::Into) -> Self { return Self::Json(message.into()); } /// Creates a tracing error value. pub fn tracing(message: impl std::convert::Into) -> Self { return Self::Tracing(message.into()); } /// Creates a Tauri runtime error value. pub fn tauri(message: impl std::convert::Into) -> Self { return Self::Tauri(message.into()); } /// Creates an HTTP transport error value. pub fn http(message: impl std::convert::Into) -> Self { return Self::Http(message.into()); } /// Creates a WebSocket transport error value. pub fn ws(message: impl std::convert::Into) -> Self { return Self::Ws(message.into()); } /// Creates a database error value. pub fn db(message: impl std::convert::Into) -> Self { return Self::Db(message.into()); } /// Creates an invalid state error value. pub fn invalid_state(message: impl std::convert::Into) -> Self { return Self::InvalidState(message.into()); } /// Creates a not-connected error value. pub fn not_connected(message: impl std::convert::Into) -> Self { return Self::NotConnected(message.into()); } /// Creates a not-implemented error value. pub fn not_implemented(message: impl std::convert::Into) -> Self { return Self::NotImplemented(message.into()); } /// Returns a stable code for logs and UI diagnostics. pub fn code(&self) -> &str { return match self { Self::Config(_) => "config", Self::Io(_) => "io", Self::Json(_) => "json", Self::Tracing(_) => "tracing", Self::Tauri(_) => "tauri", Self::Http(_) => "http", Self::Ws(_) => "ws", Self::Db(_) => "db", Self::InvalidState(_) => "invalid_state", Self::NotConnected(_) => "not_connected", Self::NotImplemented(_) => "not_implemented", Self::Custom { code, message: _ } => code.as_str(), }; } /// Returns the human-readable message without the family prefix. pub fn message(&self) -> &str { return match self { Self::Config(message) => message, Self::Io(message) => message, Self::Json(message) => message, Self::Tracing(message) => message, Self::Tauri(message) => message, Self::Http(message) => message, Self::Ws(message) => message, Self::Db(message) => message, Self::InvalidState(message) => message, Self::NotConnected(message) => message, Self::NotImplemented(message) => message, Self::Custom { code: _, message } => message, }; } } impl std::fmt::Display for crate::Error { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { return match self { Self::Config(message) => write!(formatter, "configuration error: {message}"), Self::Io(message) => write!(formatter, "io error: {message}"), Self::Json(message) => write!(formatter, "json error: {message}"), Self::Tracing(message) => write!(formatter, "tracing error: {message}"), Self::Tauri(message) => write!(formatter, "tauri error: {message}"), Self::Http(message) => write!(formatter, "http error: {message}"), Self::Ws(message) => write!(formatter, "websocket error: {message}"), Self::Db(message) => write!(formatter, "database error: {message}"), Self::InvalidState(message) => write!(formatter, "invalid state: {message}"), Self::NotConnected(message) => write!(formatter, "not connected: {message}"), Self::NotImplemented(message) => { write!(formatter, "not implemented: {message}") }, Self::Custom { code, message } => write!(formatter, "{code}: {message}"), }; } } impl std::error::Error for crate::Error {} impl std::convert::From for crate::Error { fn from(error: std::io::Error) -> Self { return Self::Io(error.to_string()); } } #[cfg(test)] mod tests { #[test] fn custom_error_preserves_code_and_message() { let error = super::Error::new("sample_code", "sample message"); assert_eq!(error.code(), "sample_code"); assert_eq!(error.message(), "sample message"); assert_eq!(error.to_string(), "sample_code: sample message"); } #[test] fn family_error_formats_with_family_prefix() { let error = super::Error::config("missing profile"); assert_eq!(error.code(), "config"); assert_eq!(error.message(), "missing profile"); assert_eq!(error.to_string(), "configuration error: missing profile"); } }