Crate decrust_core

Source
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 timeouts
  • serde: Enables serialization support for error types
  • tracing: Enables integration with the tracing ecosystem

§🎨 Best Practices

  1. Use specific error variants for different error categories
  2. Add rich context with decrust_context_msg() for better debugging
  3. Implement circuit breakers for external service calls
  4. Use macros for common error patterns to reduce boilerplate
  5. Configure error reporting for production monitoring
  6. 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Β§

OptionalError
Wrapper for Option<Box<dyn Error>> to make it compatible with backtrace

EnumsΒ§

DecrustError
Unified error type for Decrust.

TraitsΒ§

DecrustOptionExt
Extension trait for Option types to convert to Result with DecrustError
DecrustOptionExtConvenience
Convenience trait for backward compatibility with generic string types
DecrustResultExt
Extension trait for Result types to add context to errors
DecrustResultExtConvenience
Convenience trait for backward compatibility with generic string types
InfallibleResultExt
Extension trait for Results that are known to always be Err

Type AliasesΒ§

DiagnosticResult
A Result type specialized for diagnostic operations that can return multiple errors
Result
A Result type specialized for DecrustError