//! Error handling for {{TOOL_NAME}} //! //! Uses canonical error struct pattern following Microsoft Pragmatic Rust Guidelines. use std::error::Error; use std::fmt; use std::backtrace::Backtrace; /// {{TOOL_NAME}} error result type pub type Result = std::result::Result; /// {{TOOL_NAME}} error kinds #[derive(Debug)] #[non_exhaustive] pub enum {{ToolName}}ErrorKind { /// Configuration error ConfigError(String), /// I/O error IoError(String), /// Validation error ValidationError(String), /// Database error DatabaseError(String), /// Unknown error Unknown(String), } impl fmt::Display for {{ToolName}}ErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::ConfigError(msg) => write!(f, "Configuration error: {}", msg), Self::IoError(msg) => write!(f, "I/O error: {}", msg), Self::ValidationError(msg) => write!(f, "Validation error: {}", msg), Self::DatabaseError(msg) => write!(f, "Database error: {}", msg), Self::Unknown(msg) => write!(f, "Unknown error: {}", msg), } } } /// {{TOOL_NAME}} error with context pub struct {{ToolName}}Error { /// Error kind pub kind: {{ToolName}}ErrorKind, /// Context about what was happening pub context: String, /// Root cause if available pub source: Option>, /// Backtrace for debugging pub backtrace: Backtrace, } impl {{ToolName}}Error { /// Create a new error with kind and context pub fn new(kind: {{ToolName}}ErrorKind, context: impl Into) -> Self { Self { kind, context: context.into(), source: None, backtrace: Backtrace::capture(), } } /// Add source error pub fn with_source(mut self, source: Box) -> Self { self.source = Some(source); self } } impl fmt::Debug for {{ToolName}}Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("{{ToolName}}Error") .field("kind", &self.kind) .field("context", &self.context) .field("source", &self.source) .finish() } } impl fmt::Display for {{ToolName}}Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}: {}", self.kind, self.context) } } impl Error for {{ToolName}}Error { fn source(&self) -> Option<&(dyn Error + 'static)> { self.source.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_error_creation() { let err = {{ToolName}}Error::new( {{ToolName}}ErrorKind::ConfigError("test".into()), "test context", ); assert_eq!(err.context, "test context"); assert!(err.source.is_none()); } #[test] fn test_error_display() { let err = {{ToolName}}Error::new( {{ToolName}}ErrorKind::ValidationError("invalid input".into()), "during validation", ); let msg = format!("{}", err); assert!(msg.contains("invalid input")); assert!(msg.contains("during validation")); } }