// file: crates/ksp-core-lib/src/error.rs // version: 1 /// Stable structured identifier for a KSP error. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct ErrorCode { domain: &'static str, code: &'static str, } impl ErrorCode { /// Creates an error code from a stable domain and code identifier. #[must_use] pub const fn new(domain: &'static str, code: &'static str) -> Self { return Self { domain, code }; } /// Returns the stable error domain identifier. #[must_use] pub const fn domain(&self) -> &'static str { return self.domain; } /// Returns the stable error code identifier within the domain. #[must_use] pub const fn code(&self) -> &'static str { return self.code; } } /// Structured contextual field attached to a KSP error. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ErrorContext { key: &'static str, value: std::string::String, } impl ErrorContext { /// Creates one contextual field from a stable key and an owned value. #[must_use] pub fn new(key: &'static str, value: impl std::convert::Into) -> Self { return Self { key, value: value.into() }; } /// Returns the stable contextual key. #[must_use] pub fn key(&self) -> &'static str { return self.key; } /// Returns the contextual value. #[must_use] pub fn value(&self) -> &str { return self.value.as_str(); } } /// Common KSP error carrying a stable code, human-readable message, structured context and optional source. #[derive(Debug)] pub struct Error { code: crate::ErrorCode, message: std::string::String, context: std::vec::Vec, source: std::option::Option>, } impl Error { /// Creates a KSP error without context or external source. #[must_use] pub fn new(code: crate::ErrorCode, message: impl std::convert::Into) -> Self { return Self { code, message: message.into(), context: std::vec::Vec::new(), source: std::option::Option::None }; } /// Returns the stable structured error code. #[must_use] pub const fn code(&self) -> crate::ErrorCode { return self.code; } /// Returns the human-readable diagnostic message. #[must_use] pub fn message(&self) -> &str { return self.message.as_str(); } /// Returns the contextual fields in insertion order. #[must_use] pub fn context(&self) -> &[crate::ErrorContext] { return self.context.as_slice(); } /// Appends one contextual field and returns the enriched error. #[must_use] pub fn with_context(mut self, key: &'static str, value: impl std::convert::Into) -> Self { self.context.push(crate::ErrorContext::new(key, value)); return self; } /// Attaches an external error as the standard source and returns the enriched error. #[must_use] pub fn with_source(mut self, source: E) -> Self where E: std::error::Error + std::marker::Send + std::marker::Sync + 'static, { self.source = std::option::Option::Some(std::boxed::Box::new(source)); return self; } } impl std::fmt::Display for Error { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { return write!(formatter, "{}.{}: {}", self.code.domain(), self.code.code(), self.message); } } impl std::error::Error for Error { fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> { return match self.source.as_deref() { std::option::Option::Some(source) => std::option::Option::Some(source), std::option::Option::None => std::option::Option::None, }; } } /// Common result type returned by KSP APIs using [`crate::Error`]. pub type Result = std::result::Result; #[cfg(test)] #[path = "../unit_tests/error.rs"] mod tests;