//! Structured error shared by all Core services. //! //! The JSON shape (`code`, `message` and optional detail fields) matches the //! Electron 0.1.0 contract (`SessionError.toJSON`, diagnostics/biometric //! envelopes), so the unchanged renderer code keeps working. use serde::Serialize; use serde_json::{Map, Value}; use std::fmt; #[derive(Debug, Clone, Serialize)] pub struct CoreError { pub code: String, pub message: String, #[serde(flatten)] pub details: Map, } pub type CoreResult = Result; impl CoreError { pub fn new(code: impl Into, message: impl Into) -> Self { Self { code: code.into(), message: message.into(), details: Map::new() } } /// Error without a public code (Electron handlers that only threw `new Error(message)`). pub fn plain(message: impl Into) -> Self { Self::new("", message) } pub fn with(mut self, key: &str, value: impl Into) -> Self { self.details.insert(key.to_string(), value.into()); self } pub fn has_code(&self) -> bool { !self.code.is_empty() } /// Message with a `CODE: ` prefix, as the plugin runtime of 0.1.0 exposed it. pub fn prefixed_message(&self) -> String { if self.code.is_empty() || self.message.starts_with(&format!("{}:", self.code)) { self.message.clone() } else { format!("{}: {}", self.code, self.message) } } } impl fmt::Display for CoreError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.code.is_empty() { write!(f, "{}", self.message) } else { write!(f, "{}: {}", self.code, self.message) } } } impl std::error::Error for CoreError {} impl From for CoreError { fn from(error: std::io::Error) -> Self { let code = match error.kind() { std::io::ErrorKind::NotFound => "ENOENT", std::io::ErrorKind::PermissionDenied => "EACCES", std::io::ErrorKind::AlreadyExists => "EEXIST", _ => "EIO", }; CoreError::new(code, error.to_string()) } } impl From for CoreError { fn from(error: serde_json::Error) -> Self { CoreError::new("INVALID_JSON", error.to_string()) } } #[macro_export] macro_rules! bail { ($code:expr, $($arg:tt)*) => { return Err($crate::core::error::CoreError::new($code, format!($($arg)*))) }; }