198 lines
7.3 KiB
Rust
198 lines
7.3 KiB
Rust
// file: kb-core/src/error.rs
|
|
// version: 6
|
|
|
|
//! Error and result primitives shared by the workspace.
|
|
|
|
/// Workspace-wide result alias.
|
|
pub type Result<T> = std::result::Result<T, crate::Error>;
|
|
|
|
/// 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<std::string::String>,
|
|
message: impl std::convert::Into<std::string::String>,
|
|
) -> Self {
|
|
return Self::Custom {
|
|
code: code.into(),
|
|
message: message.into(),
|
|
};
|
|
}
|
|
|
|
/// Creates a configuration error value.
|
|
pub fn config(message: impl std::convert::Into<std::string::String>) -> Self {
|
|
return Self::Config(message.into());
|
|
}
|
|
|
|
/// Creates an I/O error value.
|
|
pub fn io(message: impl std::convert::Into<std::string::String>) -> Self {
|
|
return Self::Io(message.into());
|
|
}
|
|
|
|
/// Creates a JSON error value.
|
|
pub fn json(message: impl std::convert::Into<std::string::String>) -> Self {
|
|
return Self::Json(message.into());
|
|
}
|
|
|
|
/// Creates a tracing error value.
|
|
pub fn tracing(message: impl std::convert::Into<std::string::String>) -> Self {
|
|
return Self::Tracing(message.into());
|
|
}
|
|
|
|
/// Creates a Tauri runtime error value.
|
|
pub fn tauri(message: impl std::convert::Into<std::string::String>) -> Self {
|
|
return Self::Tauri(message.into());
|
|
}
|
|
|
|
/// Creates an HTTP transport error value.
|
|
pub fn http(message: impl std::convert::Into<std::string::String>) -> Self {
|
|
return Self::Http(message.into());
|
|
}
|
|
|
|
/// Creates a WebSocket transport error value.
|
|
pub fn ws(message: impl std::convert::Into<std::string::String>) -> Self {
|
|
return Self::Ws(message.into());
|
|
}
|
|
|
|
/// Creates a database error value.
|
|
pub fn db(message: impl std::convert::Into<std::string::String>) -> Self {
|
|
return Self::Db(message.into());
|
|
}
|
|
|
|
/// Creates an invalid state error value.
|
|
pub fn invalid_state(message: impl std::convert::Into<std::string::String>) -> Self {
|
|
return Self::InvalidState(message.into());
|
|
}
|
|
|
|
/// Creates a not-connected error value.
|
|
pub fn not_connected(message: impl std::convert::Into<std::string::String>) -> Self {
|
|
return Self::NotConnected(message.into());
|
|
}
|
|
|
|
/// Creates a not-implemented error value.
|
|
pub fn not_implemented(message: impl std::convert::Into<std::string::String>) -> 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<std::io::Error> 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");
|
|
}
|
|
}
|