Expand description
Β§Decrust: Advanced Error Handling Framework for Rust
Decrust is a comprehensive, production-ready error handling framework that provides rich error context, automatic error recovery, circuit breaker patterns, and powerful debugging capabilities. Itβs designed to make error handling in Rust applications both robust and developer-friendly.
Β§π Quick Start
use decrust_core::{DecrustError, DecrustResultExt, DecrustOptionExt, oops, validation_error};
// Basic error creation with rich context
fn process_user_data(data: Option<&str>) -> Result<String, DecrustError> {
let user_data = data.decrust_ok_or_missing_value("user data")?;
if user_data.is_empty() {
return Err(validation_error!("user_data", "Data cannot be empty"));
}
// Simulate an IO operation that might fail
std::fs::read_to_string("config.json")
.map_err(|e| oops!("Failed to read configuration", e))
.and_then(|_| Ok(format!("Processed: {}", user_data)))
}
Β§π― Core Features
Β§1. Rich Error Context π
Every error includes comprehensive context with location tracking, severity levels, and metadata for better debugging and monitoring.
use decrust_core::{error_context, types::ErrorSeverity, oops};
// Create rich error context with metadata
let context = error_context!(
"Database connection failed",
severity: ErrorSeverity::Critical
).with_component("database")
.with_correlation_id("req-123")
.with_recovery_suggestion("Check database connectivity");
// Use in error creation
let io_error = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "Connection refused");
let error = oops!("Database unavailable", io_error, severity: ErrorSeverity::Critical);
Β§2. Circuit Breaker Pattern β‘
Built-in circuit breaker for handling external service failures gracefully.
use decrust_core::{circuit_breaker::{CircuitBreaker, CircuitBreakerConfig}, DecrustError, Backtrace};
use std::time::Duration;
// Configure circuit breaker
let config = CircuitBreakerConfig {
failure_threshold: 5,
reset_timeout: Duration::from_secs(30),
operation_timeout: Some(Duration::from_secs(5)),
..Default::default()
};
let circuit_breaker = CircuitBreaker::new("external-api", config);
// Execute operations through circuit breaker
let result = circuit_breaker.execute(|| {
// Your external service call here
external_api_call().map_err(|e| DecrustError::Oops {
message: "API call failed".to_string(),
source: Box::new(e),
backtrace: Backtrace::generate(),
})
});
Β§3. Automatic Error Recovery π
Smart error recovery with configurable retry strategies and fix suggestions.
use decrust_core::{DecrustError, decrust::{Decrust, AutocorrectableError}, Backtrace};
let mut decrust = Decrust::new();
// Register custom fix generators
// decrust.register_fix_generator(Box::new(CustomFixGenerator::new()));
// Apply fixes automatically
if let Some(fix) = decrust.suggest_autocorrection(&error, None) {
println!("Suggested fix: {}", fix.description);
}
Β§4. Powerful Macros π οΈ
Ergonomic macros for common error handling patterns.
use decrust_core::{oops, validation_error, error_context, location, types::ErrorSeverity};
// Quick error creation
let error = oops!("Something went wrong", source_error);
// Validation errors with suggestions
let validation_err = validation_error!(
"email",
"Invalid email format",
suggestion: "Use format: user@domain.com"
);
// Rich context with location tracking
let context = error_context!("Operation failed", severity: ErrorSeverity::Error);
let loc = location!(context: "user authentication", function: "login");
Β§5. Comprehensive Error Types π
Pre-built error variants for common scenarios with rich metadata.
use decrust_core::{DecrustError, Backtrace, OptionalError};
use std::time::Duration;
// Network errors with retry information
let network_error = DecrustError::Network {
source: Box::new(std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "refused")),
kind: "HTTP".to_string(),
url: Some("https://api.example.com".to_string()),
backtrace: Backtrace::generate(),
};
// Configuration errors with suggestions
let config_error = DecrustError::Config {
message: "Invalid database URL format".to_string(),
path: Some("config.toml".into()),
source: OptionalError(None),
backtrace: Backtrace::generate(),
};
Β§π§ Advanced Usage Patterns
Β§Creating Custom Error Types
You can create domain-specific error types that integrate seamlessly with Decrust:
use decrust_core::{DecrustError, DecrustResultExt, types::ErrorSeverity, Backtrace, OptionalError};
// Define your domain-specific error
#[derive(Debug)]
pub enum UserServiceError {
NotFound { id: String },
InvalidEmail { email: String },
PermissionDenied { user_id: String },
}
impl std::fmt::Display for UserServiceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UserServiceError::NotFound { id } => write!(f, "User not found: {}", id),
UserServiceError::InvalidEmail { email } => write!(f, "Invalid email format: {}", email),
UserServiceError::PermissionDenied { user_id } => write!(f, "Permission denied for user: {}", user_id),
}
}
}
impl std::error::Error for UserServiceError {}
// Convert to DecrustError with rich context
impl From<UserServiceError> for DecrustError {
fn from(err: UserServiceError) -> Self {
match err {
UserServiceError::NotFound { id } => DecrustError::NotFound {
resource_type: "User".to_string(),
identifier: id,
backtrace: Backtrace::generate(),
},
UserServiceError::InvalidEmail { email } => DecrustError::Validation {
field: "email".to_string(),
message: format!("Invalid email format: {}", email),
expected: None,
actual: None,
rule: None,
backtrace: Backtrace::generate(),
},
UserServiceError::PermissionDenied { user_id } => {
DecrustError::ExternalService {
service_name: "UserService".to_string(),
message: format!("Permission denied for user: {}", user_id),
source: OptionalError(None),
backtrace: Backtrace::generate(),
}
}
}
}
}
// Usage in your application
fn get_user(id: &str) -> Result<User, DecrustError> {
// Your business logic here
if id.is_empty() {
return Err(UserServiceError::NotFound { id: id.to_string() }.into());
}
// Add rich context to any errors
database_call()
.map_err(|e| DecrustError::Oops {
message: "Database query failed".to_string(),
source: Box::new(e),
backtrace: Backtrace::generate(),
})
.decrust_context_msg("Fetching user from database")?;
Ok(User)
}
Β§Error Reporting and Monitoring
use decrust_core::{ErrorReporter, ErrorReportConfig, types::ErrorReportFormat, DecrustError, Backtrace};
// Configure error reporting
let config = ErrorReportConfig {
format: ErrorReportFormat::Json,
include_backtrace: true,
include_rich_context: true,
..Default::default()
};
let reporter = ErrorReporter::new();
// Report errors with rich context
let report = reporter.report_to_string(&error, &config);
println!("Error Report: {}", report);
Β§Circuit Breaker with Custom Policies
use decrust_core::{circuit_breaker::{CircuitBreaker, CircuitBreakerConfig}, DecrustError};
use std::time::Duration;
// Advanced circuit breaker configuration
let config = CircuitBreakerConfig {
failure_threshold: 3, // Open after 3 failures
success_threshold_to_close: 2, // Close after 2 successes in half-open
reset_timeout: Duration::from_secs(60), // Try half-open after 60 seconds
operation_timeout: Some(Duration::from_secs(10)), // Individual operation timeout
half_open_max_concurrent_operations: 1, // Only 1 operation in half-open
..Default::default()
};
let circuit_breaker = CircuitBreaker::new("payment-service", config);
// Use with async operations (when std-thread feature is enabled)
let result = circuit_breaker.execute(|| {
// Your potentially failing operation
call_payment_service()
});
match result {
Ok(response) => println!("Payment successful: {:?}", response),
Err(DecrustError::CircuitBreakerOpen { retry_after, .. }) => {
println!("Circuit breaker is open, retry after: {:?}", retry_after);
}
Err(e) => println!("Payment failed: {}", e),
}
Β§Object-Safe Extension Trait Usage
The extension traits are object-safe and support dynamic dispatch:
use decrust_core::{DecrustResultExt, DecrustOptionExt, DecrustError};
// Object-safe trait usage with dynamic dispatch
fn process_with_dyn_traits(
result: &dyn DecrustResultExt<String, std::io::Error>,
option: &dyn DecrustOptionExt<i32>
) {
// These work because the traits are object-safe
}
// Regular usage for better error handling
fn process_data() -> Result<String, DecrustError> {
let result: Result<String, std::io::Error> = Ok("test".to_string());
let option: Option<i32> = Some(42);
// Add context to results (object-safe methods)
let processed = result.decrust_context_msg("Processing data")?;
// Convert options to results with meaningful errors (object-safe methods)
let value = option.decrust_ok_or_missing_value("required value")?;
Ok(format!("{} - {}", processed, value))
}
Β§π Feature Flags
std-thread
: Enables threading support for circuit breaker timeoutsserde
: Enables serialization support for error typestracing
: Enables integration with the tracing ecosystem
Β§π¨ Best Practices
- Use specific error variants for different error categories
- Add rich context with
decrust_context_msg()
for better debugging - Implement circuit breakers for external service calls
- Use macros for common error patterns to reduce boilerplate
- Configure error reporting for production monitoring
- Create domain-specific error types that convert to
DecrustError
Β§π Integration Examples
Β§With Tokio and Async
use decrust_core::{DecrustError, DecrustResultExt, Backtrace};
use std::path::PathBuf;
// Simulate async file reading without requiring tokio dependency
async fn read_config_async() -> Result<String, DecrustError> {
// Simulate reading a config file
let result = std::fs::read_to_string("Cargo.toml") // Use existing file
.map_err(|e| DecrustError::Io {
source: e,
path: Some("Cargo.toml".into()),
operation: "read config file".to_string(),
backtrace: Backtrace::generate(),
})
.decrust_context_msg("Loading application configuration")?;
Ok(result)
}
// Test the async function (without actually running it)
let _future = read_config_async();
Β§With Configuration Parsing
use decrust_core::{DecrustError, Backtrace, OptionalError};
use std::path::PathBuf;
// Simple configuration struct (without serde dependency)
struct AppConfig {
database_url: String,
api_key: String,
}
fn load_config() -> Result<AppConfig, DecrustError> {
// Simulate reading configuration from Cargo.toml (which exists)
let config_str = std::fs::read_to_string("Cargo.toml")
.map_err(|e| DecrustError::Config {
message: "Failed to read configuration file".to_string(),
path: Some("Cargo.toml".into()),
source: OptionalError::new(Some(Box::new(e))),
backtrace: Backtrace::generate(),
})?;
// Simulate parsing (just create a dummy config)
let config = AppConfig {
database_url: "postgresql://localhost/mydb".to_string(),
api_key: "dummy_key".to_string(),
};
// Validate configuration
if config.database_url.is_empty() {
return Err(DecrustError::Validation {
field: "database_url".to_string(),
message: "Database URL cannot be empty".to_string(),
expected: None,
actual: None,
rule: None,
backtrace: Backtrace::generate(),
});
}
Ok(config)
}
// Test the function
let _config = load_config();
Re-exportsΒ§
pub use self::backtrace::AsBacktrace;
pub use self::backtrace::BacktraceCompat;
pub use self::backtrace::BacktraceFrame;
pub use self::backtrace::BacktraceProvider;
pub use self::backtrace::BacktraceStatus;
pub use self::backtrace::DecrustBacktrace as Backtrace;
pub use self::backtrace::GenerateImplicitData;
pub use self::backtrace::Location;
pub use self::backtrace::ThreadId;
pub use self::backtrace::Timestamp;
pub use self::circuit_breaker::CircuitBreaker;
pub use self::circuit_breaker::CircuitBreakerConfig;
pub use self::circuit_breaker::CircuitBreakerObserver;
pub use self::circuit_breaker::CircuitBreakerState;
pub use self::circuit_breaker::CircuitMetrics;
pub use self::circuit_breaker::CircuitOperationType;
pub use self::circuit_breaker::CircuitTransitionEvent;
pub use self::decrust::AstMissingImportFixGenerator;
pub use self::decrust::AstUnusedCodeFixGenerator;
pub use self::decrust::AutocorrectableError;
pub use self::decrust::ClosureCaptureLifetimeFixGenerator;
pub use self::decrust::ConfigMissingKeyFixGenerator;
pub use self::decrust::ConfigSyntaxFixGenerator;
pub use self::decrust::CrateUsageAnalysis;
pub use self::decrust::Decrust;
pub use self::decrust::DependencyAnalysisResult;
pub use self::decrust::DependencyAnalyzer;
pub use self::decrust::DivisionByZeroFixGenerator;
pub use self::decrust::InteractiveRecommendation;
pub use self::decrust::InvalidArgumentCountFixGenerator;
pub use self::decrust::IoMissingDirectoryFixGenerator;
pub use self::decrust::IoPermissionFixGenerator;
pub use self::decrust::JsonParseFixGenerator;
pub use self::decrust::MissingOkErrFixGenerator;
pub use self::decrust::NetworkConnectionFixGenerator;
pub use self::decrust::NetworkTlsFixGenerator;
pub use self::decrust::OptimizationImpact;
pub use self::decrust::QuestionMarkPropagationFixGenerator;
pub use self::decrust::RecommendationType;
pub use self::decrust::RecursiveTypeFixGenerator;
pub use self::decrust::ReturnLocalReferenceFixGenerator;
pub use self::decrust::RuntimePanicFixGenerator;
pub use self::decrust::SecurityImpact;
pub use self::decrust::UnnecessaryCloneFixGenerator;
pub use self::decrust::UnnecessaryParenthesesFixGenerator;
pub use self::decrust::UnsafeUnwrapFixGenerator;
pub use self::decrust::UnstableFeatureFixGenerator;
pub use self::decrust::UnusedMutFixGenerator;
pub use self::decrust::VersionCompatibility;
pub use self::decrust::YamlParseFixGenerator;
pub use self::reporter::ErrorReportConfig;
pub use self::reporter::ErrorReporter;
pub use self::syntax::FixTemplate;
pub use self::syntax::SyntaxGenerator;
pub use self::syntax::TemplateRegistry;
pub use self::types::Autocorrection;
pub use self::types::ErrorCategory;
pub use self::types::ErrorContext;
pub use self::types::ErrorReportFormat;
pub use self::types::ErrorSeverity;
pub use self::types::ErrorSource;
pub use self::types::ExtractedParameters;
pub use self::types::FixDetails;
pub use self::types::FixType;
pub use self::types::ParameterExtractor;
pub use self::types::ParameterSource;
ModulesΒ§
- backtrace
- Brief: Direct backtrace implementation with custom GenerateImplicitData trait.
- circuit_
breaker - Brief: Circuit breaker implementation for resilience.
- decrust
- Brief: Decrust autocorrection framework integration.
- reporter
- Brief: Error reporting utilities for structured error displays.
- syntax
- [Syntax Generation Module]
- types
- Brief: Core error-related structs and types for the error handling framework.
MacrosΒ§
- error_
context - Macro to create a comprehensive error context with location and metadata
- implicit_
data - Macro to generate implicit data at the call site
- location
- Macro to create a location at the current source position
- oops
- Macro to create a DecrustError::Oops with rich context
- validation_
error - Macro to create a quick validation error
StructsΒ§
- Optional
Error - Wrapper for
Option<Box<dyn Error>>
to make it compatible with backtrace
EnumsΒ§
- Decrust
Error - Unified error type for Decrust.
TraitsΒ§
- Decrust
Option Ext - Extension trait for Option types to convert to Result with DecrustError
- Decrust
Option ExtConvenience - Convenience trait for backward compatibility with generic string types
- Decrust
Result Ext - Extension trait for Result types to add context to errors
- Decrust
Result ExtConvenience - Convenience trait for backward compatibility with generic string types
- Infallible
Result Ext - Extension trait for Results that are known to always be Err
Type AliasesΒ§
- Diagnostic
Result - A Result type specialized for diagnostic operations that can return multiple errors
- Result
- A Result type specialized for DecrustError