|
12 | 12 | <span> </span> |
13 | 13 | <a href="https://github.com/jamesgober/error-forge/actions"><img alt="GitHub CI" src="https://github.com/jamesgober/error-forge/actions/workflows/ci.yml/badge.svg"></a> |
14 | 14 | </div> |
15 | | -<br> |
| 15 | +<br> |
| 16 | + |
| 17 | +# Error Forge |
| 18 | + |
| 19 | +Error Forge is a comprehensive, zero-dependency error management framework for Rust applications. It simplifies error handling through expressive macros, automatic trait implementations, and extensible error hooks for seamless integration with external logging systems. |
| 20 | + |
| 21 | +## Features |
| 22 | + |
| 23 | +- **Rich Error Types**: Define expressive error types with minimal boilerplate |
| 24 | +- **ForgeError Trait**: Unified interface for all error types with contextual metadata |
| 25 | +- **Declarative Macros**: Generate complete error enums with the `define_errors!` macro |
| 26 | +- **Error Composition**: Combine errors from multiple modules with the `group!` macro |
| 27 | +- **Derive Macros**: Quickly implement errors with `#[derive(ModError)]` |
| 28 | +- **Console Formatting**: ANSI color formatting for terminal output with `ConsoleTheme` |
| 29 | +- **Error Hooks**: Register callbacks for errors with severity level support |
| 30 | +- **Zero External Dependencies**: Completely standalone with no third-party dependencies |
| 31 | + |
| 32 | +## Installation |
| 33 | + |
| 34 | +Add the following to your `Cargo.toml` file: |
| 35 | + |
| 36 | +```toml |
| 37 | +[dependencies] |
| 38 | +error-forge = "0.6.0" |
| 39 | +``` |
| 40 | + |
| 41 | +## Usage |
| 42 | + |
| 43 | +### Basic Error Definition |
| 44 | + |
| 45 | +```rust |
| 46 | +use error_forge::define_errors; |
| 47 | + |
| 48 | +define_errors! { |
| 49 | + pub enum DatabaseError { |
| 50 | + #[error(display = "Database connection failed: {}", message)] |
| 51 | + ConnectionFailed { message: String }, |
| 52 | + |
| 53 | + #[error(display = "Query execution failed: {}", message)] |
| 54 | + QueryFailed { message: String, query: String }, |
| 55 | + |
| 56 | + #[error(display = "Record not found with ID: {}", id)] |
| 57 | + RecordNotFound { id: String }, |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +// The macro automatically implements constructors and the ForgeError trait |
| 62 | +fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 63 | + // Create an error using the generated constructor |
| 64 | + let error = DatabaseError::connection_failed("Timeout after 30 seconds"); |
| 65 | + |
| 66 | + // Use ForgeError methods |
| 67 | + println!("Error kind: {}", error.kind()); |
| 68 | + println!("Caption: {}", error.caption()); |
| 69 | + println!("Status code: {}", error.status_code()); |
| 70 | + |
| 71 | + Err(Box::new(error)) |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +### Error Composition with the `group!` Macro |
| 76 | + |
| 77 | +```rust |
| 78 | +use error_forge::{define_errors, group}; |
| 79 | + |
| 80 | +define_errors! { |
| 81 | + pub enum ApiError { |
| 82 | + #[error(display = "Invalid API key")] |
| 83 | + InvalidApiKey, |
| 84 | + |
| 85 | + #[error(display = "Rate limit exceeded")] |
| 86 | + RateLimitExceeded, |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +define_errors! { |
| 91 | + pub enum ValidationError { |
| 92 | + #[error(display = "Required field {} is missing", field)] |
| 93 | + MissingField { field: String }, |
| 94 | + |
| 95 | + #[error(display = "Field {} has invalid format", field)] |
| 96 | + InvalidFormat { field: String }, |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +// Combine errors from different modules into a single error type |
| 101 | +group! { |
| 102 | + pub enum AppError { |
| 103 | + Api(ApiError), |
| 104 | + Validation(ValidationError), |
| 105 | + // Add more error types as needed |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +fn validate_request() -> Result<(), AppError> { |
| 110 | + // Create a ValidationError |
| 111 | + let error = ValidationError::missing_field("username"); |
| 112 | + |
| 113 | + // The error will be automatically converted to AppError |
| 114 | + Err(error.into()) |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +### Using Derive Macro |
| 119 | + |
| 120 | +```rust |
| 121 | +use error_forge::ModError; |
| 122 | + |
| 123 | +#[derive(Debug, ModError)] |
| 124 | +#[module_error(kind = "AuthError")] |
| 125 | +pub enum AuthError { |
| 126 | + #[error(display = "Invalid credentials")] |
| 127 | + InvalidCredentials, |
| 128 | + |
| 129 | + #[error(display = "Account locked: {}", reason)] |
| 130 | + AccountLocked { reason: String }, |
| 131 | + |
| 132 | + #[error(display = "Session expired")] |
| 133 | + #[http_status(401)] |
| 134 | + SessionExpired, |
| 135 | +} |
| 136 | + |
| 137 | +fn login() -> Result<(), AuthError> { |
| 138 | + Err(AuthError::InvalidCredentials) |
| 139 | +} |
| 140 | +``` |
| 141 | + |
| 142 | +### Error Hooks for Logging Integration |
| 143 | + |
| 144 | +```rust |
| 145 | +use error_forge::{AppError, macros::{register_error_hook, ErrorLevel, ErrorContext}}; |
| 146 | + |
| 147 | +fn main() { |
| 148 | + // Register a hook for centralized error handling |
| 149 | + register_error_hook(|ctx| { |
| 150 | + match ctx.level { |
| 151 | + ErrorLevel::Info => println!("INFO: {} [{}]", ctx.caption, ctx.kind), |
| 152 | + ErrorLevel::Warning => println!("WARN: {} [{}]", ctx.caption, ctx.kind), |
| 153 | + ErrorLevel::Error => println!("ERROR: {} [{}]", ctx.caption, ctx.kind), |
| 154 | + ErrorLevel::Critical => { |
| 155 | + println!("CRITICAL: {} [{}]", ctx.caption, ctx.kind); |
| 156 | + |
| 157 | + // Send notifications for critical errors |
| 158 | + if ctx.is_fatal { |
| 159 | + send_notification("Critical error occurred", ctx.caption); |
| 160 | + } |
| 161 | + } |
| 162 | + } |
| 163 | + }); |
| 164 | + |
| 165 | + // This will trigger the hook |
| 166 | + let _error = AppError::config("Configuration file not found"); |
| 167 | +} |
| 168 | + |
| 169 | +fn send_notification(level: &str, message: &str) { |
| 170 | + // Send notifications via your preferred channel |
| 171 | + println!("Notification sent: {} - {}", level, message); |
| 172 | +} |
| 173 | +``` |
| 174 | + |
| 175 | +### Console Formatting |
| 176 | + |
| 177 | +```rust |
| 178 | +use error_forge::{AppError, ConsoleTheme}; |
| 179 | + |
| 180 | +fn main() { |
| 181 | + // Create an error |
| 182 | + let error = AppError::config("Database configuration missing"); |
| 183 | + |
| 184 | + // Set up console theme |
| 185 | + let theme = ConsoleTheme::new(); |
| 186 | + |
| 187 | + // Print formatted error |
| 188 | + println!("{}", theme.format_error(&error)); |
| 189 | + |
| 190 | + // Install panic hook for consistent formatting |
| 191 | + theme.install_panic_hook(); |
| 192 | + |
| 193 | + // This panic will be formatted consistently with other errors |
| 194 | + panic!("Something went wrong!"); |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +## Advanced Usage |
| 199 | + |
| 200 | +For more detailed documentation and advanced usage examples, refer to the [API Documentation](docs/API.md). |
0 commit comments