Exemplos de Tratamento de Erros Web Rust

Exemplos de tratamento de erros Web Rust incluindo captura de exceções, registro e validação de parâmetros

💻 Registro de Logs rust

🟢 simple ⭐⭐⭐

Escrever logs em arquivos e console com diferentes níveis

⏱️ 20 min 🏷️ rust, web, error handling
Prerequisites: Basic Rust, chrono crate
// Web Rust Logging Examples
// Log writing to files and console
//
// Add to Cargo.toml:
// [dependencies]
// log = "0.4"
// env_logger = "0.10"
// chrono = "0.4"

use std::fs::{File, OpenOptions};
use std::io::Write;
use std::path::Path;

// 1. Simple Logging to File

/// Log message to file
fn log_to_file(path: &str, message: &str) -> std::io::Result<()> {
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(path)?;
    writeln!(file, "{}", message)?;
    Ok(())
}

/// Log with timestamp
fn log_with_timestamp(path: &str, level: &str, message: &str) -> std::io::Result<()> {
    let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
    let log_entry = format!("[{}] {} - {}", timestamp, level, message);
    log_to_file(path, &log_entry)
}

// 2. Log Levels

/// Log debug message
fn log_debug(path: &str, message: &str) -> std::io::Result<()> {
    log_with_timestamp(path, "DEBUG", message)
}

/// Log info message
fn log_info(path: &str, message: &str) -> std::io::Result<()> {
    log_with_timestamp(path, "INFO", message)
}

/// Log warning message
fn log_warning(path: &str, message: &str) -> std::io::Result<()> {
    log_with_timestamp(path, "WARNING", message)
}

/// Log error message
fn log_error(path: &str, message: &str) -> std::io::Result<()> {
    log_with_timestamp(path, "ERROR", message)
}

// 3. Structured Logging

/// Structured log entry
#[derive(Debug)]
struct LogEntry {
    timestamp: String,
    level: String,
    message: String,
    module: Option<String>,
}

impl LogEntry {
    fn new(level: &str, message: &str) -> Self {
        LogEntry {
            timestamp: chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f").to_string(),
            level: level.to_string(),
            message: message.to_string(),
            module: None,
        }
    }

    fn with_module(mut self, module: &str) -> Self {
        self.module = Some(module.to_string());
        self
    }

    fn format(&self) -> String {
        match &self.module {
            Some(m) => format!("[{}] [{}] [{}] - {}", self.timestamp, self.level, m, self.message),
            None => format!("[{}] [{}] - {}", self.timestamp, self.level, self.message),
        }
    }
}

/// Write structured log
fn write_structured_log(path: &str, entry: &LogEntry) -> std::io::Result<()> {
    log_to_file(path, &entry.format())
}

// 4. Logger with Multiple Outputs

/// Simple logger that writes to multiple destinations
struct MultiLogger {
    log_file: String,
    console: bool,
}

impl MultiLogger {
    fn new(log_file: &str, console: bool) -> Self {
        MultiLogger {
            log_file: log_file.to_string(),
            console,
        }
    }

    fn log(&self, level: &str, message: &str) -> std::io::Result<()> {
        let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
        let log_entry = format!("[{}] {} - {}", timestamp, level, message);

        // Write to file
        if !self.log_file.is_empty() {
            log_to_file(&self.log_file, &log_entry)?;
        }

        // Write to console
        if self.console {
            println!("{}", log_entry);
        }

        Ok(())
    }

    fn info(&self, message: &str) -> std::io::Result<()> {
        self.log("INFO", message)
    }

    fn warning(&self, message: &str) -> std::io::Result<()> {
        self.log("WARNING", message)
    }

    fn error(&self, message: &str) -> std::io::Result<()> {
        self.log("ERROR", message)
    }

    fn debug(&self, message: &str) -> std::io::Result<()> {
        self.log("DEBUG", message)
    }
}

// 5. Rotating Log Files

/// Rotating logger
struct RotatingLogger {
    base_path: String,
    max_size: u64,
    current_file: String,
    current_size: u64,
}

impl RotatingLogger {
    fn new(base_path: &str, max_size: u64) -> Self {
        let current_file = format!("{}.log", base_path);
        RotatingLogger {
            base_path: base_path.to_string(),
            max_size,
            current_file: current_file.clone(),
            current_size: std::fs::metadata(&current_file).map(|m| m.len()).unwrap_or(0),
        }
    }

    fn log(&mut self, message: &str) -> std::io::Result<()> {
        let entry = format!("{}\n", message);
        let entry_size = entry.len() as u64;

        // Check if rotation needed
        if self.current_size + entry_size > self.max_size {
            self.rotate()?;
        }

        // Append to current file
        let mut file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(&self.current_file)?;
        file.write_all(entry.as_bytes())?;
        self.current_size += entry_size;

        Ok(())
    }

    fn rotate(&mut self) -> std::io::Result<()> {
        // Find next available number
        let mut counter = 1;
        loop {
            let archive_name = format!("{}.{}.log", self.base_path, counter);
            if !Path::new(&archive_name).exists() {
                std::fs::rename(&self.current_file, &archive_name)?;
                break;
            }
            counter += 1;
        }

        // Reset current file
        self.current_size = 0;
        Ok(())
    }
}

// 6. Conditional Logging

/// Logger with level filtering
struct LevelLogger {
    log_file: String,
    min_level: String,
}

impl LevelLogger {
    fn new(log_file: &str, min_level: &str) -> Self {
        LevelLogger {
            log_file: log_file.to_string(),
            min_level: min_level.to_string(),
        }
    }

    fn should_log(&self, level: &str) -> bool {
        let levels = vec!["DEBUG", "INFO", "WARNING", "ERROR"];
        let current = levels.iter().position(|&l| l == level).unwrap_or(0);
        let min = levels.iter().position(|&l| l == &self.min_level).unwrap_or(0);
        current >= min
    }

    fn log(&self, level: &str, message: &str) -> std::io::Result<()> {
        if self.should_log(level) {
            log_with_timestamp(&self.log_file, level, message)?;
        }
        Ok(())
    }
}

// 7. Async Logging (Simplified)

/// Async-style logger using channels
use std::sync::mpsc;
use std::thread;

struct AsyncLogger {
    sender: mpsc::Sender<String>,
}

impl AsyncLogger {
    fn new(log_file: &str) -> Self {
        let (sender, receiver) = mpsc::channel();
        let log_file = log_file.to_string();

        thread::spawn(move || {
            let mut file = OpenOptions::new()
                .create(true)
                .append(true)
                .open(&log_file)
                .unwrap();

            for message in receiver {
                writeln!(file, "{}", message).unwrap();
            }
        });

        AsyncLogger { sender }
    }

    fn log(&self, message: &str) -> std::io::Result<()> {
        self.sender.send(message.to_string()).map_err(|e| {
            std::io::Error::new(std::io::ErrorKind::Other, e)
        })?;
        Ok(())
    }
}

// 8. Performance Logging

/// Performance logger
fn log_performance(path: &str, operation: &str, duration_ms: u128) -> std::io::Result<()> {
    let message = format!("Operation '{}' took {}ms", operation, duration_ms);
    log_with_timestamp(path, "PERF", &message)
}

/// Measure execution time
fn measure_time<F, T>(f: F) -> T
where
    F: FnOnce() -> T,
{
    f()
}

/// Measure and log execution time
fn measure_and_log<F, T>(path: &str, operation: &str, f: F) -> T
where
    F: FnOnce() -> T,
{
    let start = std::time::Instant::now();
    let result = f();
    let duration = start.elapsed().as_millis();
    let _ = log_performance(path, operation, duration);
    result
}

// 9. Error Logging

/// Log error with backtrace
fn log_error_with_context(path: &str, error: &str, context: &str) -> std::io::Result<()> {
    let message = format!("Error in '{}': {}", context, error);
    log_with_timestamp(path, "ERROR", &message)
}

/// Log warning with details
fn log_warning_details(path: &str, message: &str, details: &str) -> std::io::Result<()> {
    let log_msg = format!("{} | Details: {}", message, details);
    log_with_timestamp(path, "WARNING", &log_msg)
}

// 10. Batch Logging

/// Log multiple messages efficiently
fn log_batch(path: &str, messages: &[&str]) -> std::io::Result<()> {
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(path)?;

    for message in messages {
        let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
        writeln!(file, "[{}] - {}", timestamp, message)?;
    }

    Ok(())
}

// Usage Examples
fn main() -> std::io::Result<()> {
    println!("=== Web Rust Logging Examples ===\n");

    let log_file = "application.log";

    // 1. Basic logging
    println!("--- 1. Basic Logging ---");
    log_to_file(log_file, "Application started")?;
    log_with_timestamp(log_file, "INFO", "System initialized")?;

    // 2. Log levels
    println!("
--- 2. Log Levels ---");
    log_debug(log_file, "Debug information")?;
    log_info(log_file, "Application running")?;
    log_warning(log_file, "High memory usage")?;
    log_error(log_file, "Connection failed")?;

    // 3. Structured logging
    println!("
--- 3. Structured Logging ---");
    let entry = LogEntry::new("INFO", "User logged in").with_module("Auth");
    write_structured_log(log_file, &entry)?;

    // 4. Multi-logger
    println!("
--- 4. Multi-Logger ---");
    let logger = MultiLogger::new("multi.log", true);
    logger.info("Multi-logger initialized")?;
    logger.warning("This goes to console and file")?;
    logger.error("Error occurred")?;

    // 5. Level filtering
    println!("
--- 5. Level Filtering ---");
    let level_logger = LevelLogger::new("filtered.log", "WARNING");
    level_logger.log("DEBUG", "This won't be logged")?;
    level_logger.log("WARNING", "This will be logged")?;
    level_logger.log("ERROR", "This will be logged")?;

    // 6. Performance logging
    println!("
--- 6. Performance Logging ---");
    let result = measure_and_log(log_file, "test_operation", || {
        std::thread::sleep(std::time::Duration::from_millis(100));
        42
    });
    println!("Result: {}", result);

    // 7. Error logging
    println!("
--- 7. Error Logging ---");
    log_error_with_context(log_file, "File not found", "load_config")?;
    log_warning_details(log_file, "Slow query", "took 5 seconds")?;

    // 8. Batch logging
    println!("
--- 8. Batch Logging ---");
    let messages = vec![
        "Message 1",
        "Message 2",
        "Message 3",
    ];
    log_batch(log_file, &messages)?;

    // 9. Rotating logger
    println!("
--- 9. Rotating Logger ---");
    let mut rotating_logger = RotatingLogger::new("rotating", 1024);
    for i in 0..10 {
        rotating_logger.log(&format!("Log entry {}", i))?;
    }

    println!("
=== All Logging Examples Completed ===");
    println!("Check {} for log output", log_file);
    Ok(())
}

💻 Validação de Parâmetros rust

🟢 simple ⭐⭐⭐

Validar parâmetros de função com regras personalizadas e mensagens de erro

⏱️ 20 min 🏷️ rust, web, error handling
Prerequisites: Basic Rust
// Web Rust Parameter Validation Examples
// Function parameter validation with custom rules

use std::fmt;

// 1. Validation Error Types

/// Validation error
#[derive(Debug, Clone, PartialEq)]
enum ValidationError {
    TooShort(String, usize),
    TooLong(String, usize),
    InvalidFormat(String),
    OutOfRange(String, String),
    Required(String),
    Custom(String),
}

impl fmt::Display for ValidationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ValidationError::TooShort(field, min) => {
                write!(f, "{} must be at least {} characters", field, min)
            },
            ValidationError::TooLong(field, max) => {
                write!(f, "{} must be at most {} characters", field, max)
            },
            ValidationError::InvalidFormat(field) => {
                write!(f, "{} has invalid format", field)
            },
            ValidationError::OutOfRange(field, range) => {
                write!(f, "{} must be in range {}", field, range)
            },
            ValidationError::Required(field) => {
                write!(f, "{} is required", field)
            },
            ValidationError::Custom(msg) => {
                write!(f, "{}", msg)
            },
        }
    }
}

impl std::error::Error for ValidationError {}

// 2. String Validation

/// Validate string length
fn validate_length(value: &str, field: &str, min: usize, max: usize) -> Result<(), ValidationError> {
    if value.is_empty() {
        return Err(ValidationError::Required(field.to_string()));
    }
    if value.len() < min {
        return Err(ValidationError::TooShort(field.to_string(), min));
    }
    if value.len() > max {
        return Err(ValidationError::TooLong(field.to_string(), max));
    }
    Ok(())
}

/// Validate email format
fn validate_email(email: &str) -> Result<(), ValidationError> {
    if email.is_empty() {
        return Err(ValidationError::Required("Email".to_string()));
    }

    // Basic email validation
    if !email.contains('@') || !email.contains('.') {
        return Err(ValidationError::InvalidFormat("Email".to_string()));
    }

    let parts: Vec<&str> = email.split('@').collect();
    if parts.len() != 2 {
        return Err(ValidationError::InvalidFormat("Email".to_string()));
    }

    let (local, domain) = (parts[0], parts[1]);
    if local.is_empty() || domain.is_empty() || !domain.contains('.') {
        return Err(ValidationError::InvalidFormat("Email".to_string()));
    }

    Ok(())
}

/// Validate URL format
fn validate_url(url: &str) -> Result<(), ValidationError> {
    if url.is_empty() {
        return Err(ValidationError::Required("URL".to_string()));
    }

    if !url.starts_with("http://") && !url.starts_with("https://") {
        return Err(ValidationError::InvalidFormat("URL".to_string()));
    }

    Ok(())
}

/// Validate username
fn validate_username(username: &str) -> Result<(), ValidationError> {
    validate_length(username, "Username", 3, 20)?;

    // Must be alphanumeric with underscores
    if !username.chars().all(|c| c.is_alphanumeric() || c == '_') {
        return Err(ValidationError::InvalidFormat("Username".to_string()));
    }

    Ok(())
}

/// Validate password
fn validate_password(password: &str) -> Result<(), ValidationError> {
    validate_length(password, "Password", 8, 128)?;

    let has_upper = password.chars().any(|c| c.is_uppercase());
    let has_lower = password.chars().any(|c| c.is_lowercase());
    let has_digit = password.chars().any(|c| c.is_ascii_digit());

    if !has_upper {
        return Err(ValidationError::Custom(
            "Password must contain at least one uppercase letter".to_string()
        ));
    }
    if !has_lower {
        return Err(ValidationError::Custom(
            "Password must contain at least one lowercase letter".to_string()
        ));
    }
    if !has_digit {
        return Err(ValidationError::Custom(
            "Password must contain at least one digit".to_string()
        ));
    }

    Ok(())
}

// 3. Numeric Validation

/// Validate number in range
fn validate_range<T: PartialOrd>(value: T, field: &str, min: T, max: T) -> Result<(), ValidationError> {
    if value < min || value > max {
        return Err(ValidationError::OutOfRange(
            field.to_string(),
            format!("{:?} to {:?}", min, max)
        ));
    }
    Ok(())
}

/// Validate positive number
fn validate_positive<T: PartialOrd + std::fmt::Debug>(value: T, field: &str) -> Result<(), ValidationError> {
    if value <= T::from(0).unwrap() {
        return Err(ValidationError::Custom(
            format!("{:?} must be positive", field)
        ));
    }
    Ok(())
}

/// Validate non-negative number
fn validate_non_negative<T: PartialOrd>(value: T, field: &str) -> Result<(), ValidationError> {
    if value < T::from(0).unwrap() {
        return Err(ValidationError::Custom(
            format!("{:?} must be non-negative", field)
        ));
    }
    Ok(())
}

// 4. Collection Validation

/// Validate collection not empty
fn validate_not_empty<T>(collection: &[T], field: &str) -> Result<(), ValidationError> {
    if collection.is_empty() {
        return Err(ValidationError::Custom(
            format!("{} must not be empty", field)
        ));
    }
    Ok(())
}

/// Validate collection size
fn validate_collection_size<T>(collection: &[T], field: &str, min: usize, max: usize) -> Result<(), ValidationError> {
    if collection.len() < min {
        return Err(ValidationError::Custom(
            format!("{} must have at least {} items", field, min)
        ));
    }
    if collection.len() > max {
        return Err(ValidationError::Custom(
            format!("{} must have at most {} items", field, max)
        ));
    }
    Ok(())
}

/// Validate all unique
fn validate_all_unique<T: std::hash::Hash + Eq + std::fmt::Debug>(collection: &[T], field: &str) -> Result<(), ValidationError> {
    use std::collections::HashSet;
    let unique: HashSet<_> = collection.iter().collect();
    if unique.len() != collection.len() {
        return Err(ValidationError::Custom(
            format!("{} must contain only unique items", field)
        ));
    }
    Ok(())
}

// 5. Date/Time Validation

/// Validate age range
fn validate_age(age: u32) -> Result<(), ValidationError> {
    if age < 0 {
        return Err(ValidationError::Custom("Age cannot be negative".to_string()));
    }
    if age > 150 {
        return Err(ValidationError::Custom("Age cannot exceed 150".to_string()));
    }
    Ok(())
}

/// Validate year range
fn validate_year(year: i32) -> Result<(), ValidationError> {
    validate_range(year, "Year", 1900, 2100)
}

// 6. Custom Validation Rules

/// Validation rule type
type ValidationRule<T> = Box<dyn Fn(&T) -> Result<(), ValidationError>>;

/// Apply multiple validation rules
fn validate_with_rules<T>(value: &T, rules: &[ValidationRule<T>]) -> Result<(), ValidationError> {
    for rule in rules {
        rule(value)?;
    }
    Ok(())
}

/// Create length rule
fn length_rule(field: &'static str, min: usize, max: usize) -> ValidationRule<String> {
    Box::new(move |value: &String| {
        validate_length(value, field, min, max)
    })
}

/// Create range rule
fn range_rule<T: PartialOrd + Copy>(field: &'static str, min: T, max: T) -> ValidationRule<T> {
    Box::new(move |value: &T| {
        validate_range(*value, field, min, max)
    })
}

// 7. Composite Validation

/// User data validation
#[derive(Debug)]
struct User {
    username: String,
    email: String,
    age: u32,
}

impl User {
    fn validate(&self) -> Result<(), Vec<ValidationError>> {
        let mut errors = Vec::new();

        if let Err(e) = validate_username(&self.username) {
            errors.push(e);
        }

        if let Err(e) = validate_email(&self.email) {
            errors.push(e);
        }

        if let Err(e) = validate_age(self.age) {
            errors.push(e);
        }

        if errors.is_empty() {
            Ok(())
        } else {
            Err(errors)
        }
    }
}

/// Product validation
#[derive(Debug)]
struct Product {
    name: String,
    price: f64,
    quantity: u32,
}

impl Product {
    fn validate(&self) -> Result<(), Vec<ValidationError>> {
        let mut errors = Vec::new();

        validate_length(&self.name, "Name", 1, 100)
            .map_err(|e| errors.push(e)).ok();

        validate_range(self.price, "Price", 0.0, 1000000.0)
            .map_err(|e| errors.push(e)).ok();

        validate_non_negative(self.quantity, "Quantity")
            .map_err(|e| errors.push(e)).ok();

        if errors.is_empty() {
            Ok(())
        } else {
            Err(errors)
        }
    }
}

// 8. Conditional Validation

/// Validate with condition
fn validate_if(condition: bool, validation: Result<(), ValidationError>) -> Result<(), ValidationError> {
    if condition {
        validation
    } else {
        Ok(())
    }
}

/// Validate required if
fn validate_required_if(value: &str, condition: bool, field: &str) -> Result<(), ValidationError> {
    validate_if(condition && value.is_empty(), Err(ValidationError::Required(field.to_string())))
}

// 9. Sanitization

/// Trim and validate
fn validate_trimmed(value: &str, field: &str, min: usize, max: usize) -> Result<(), ValidationError> {
    let trimmed = value.trim();
    validate_length(trimmed, field, min, max)
}

/// Sanitize email
fn sanitize_email(email: &str) -> String {
    email.trim().to_lowercase()
}

// 10. Validation Builder

/// Simple validation builder
struct Validator<'a, T> {
    value: &'a T,
    errors: Vec<ValidationError>,
}

impl<'a, T> Validator<'a, T> {
    fn new(value: &'a T) -> Self {
        Validator {
            value,
            errors: Vec::new(),
        }
    }

    fn add_rule<F>(mut self, rule: F) -> Self
    where
        F: FnOnce(&T) -> Result<(), ValidationError>,
    {
        if let Err(e) = rule(self.value) {
            self.errors.push(e);
        }
        self
    }

    fn validate(self) -> Result<(), Vec<ValidationError>> {
        if self.errors.is_empty() {
            Ok(())
        } else {
            Err(self.errors)
        }
    }
}

// Usage Examples
fn main() {
    println!("=== Web Rust Parameter Validation Examples ===\n");

    // 1. String validation
    println!("--- 1. String Validation ---");
    match validate_username("user123") {
        Ok(_) => println!("'user123' is valid"),
        Err(e) => println!("Error: {}", e),
    }
    match validate_username("ab") {
        Ok(_) => println!("'ab' is valid"),
        Err(e) => println!("Error: {}", e),
    }

    // 2. Email validation
    println!("
--- 2. Email Validation ---");
    match validate_email("[email protected]") {
        Ok(_) => println!("'[email protected]' is valid"),
        Err(e) => println!("Error: {}", e),
    }
    match validate_email("invalid-email") {
        Ok(_) => println!("'invalid-email' is valid"),
        Err(e) => println!("Error: {}", e),
    }

    // 3. Password validation
    println!("
--- 3. Password Validation ---");
    match validate_password("SecurePass123") {
        Ok(_) => println!("Password is valid"),
        Err(e) => println!("Error: {}", e),
    }
    match validate_password("weak") {
        Ok(_) => println!("Password is valid"),
        Err(e) => println!("Error: {}", e),
    }

    // 4. Numeric validation
    println!("
--- 4. Numeric Validation ---");
    match validate_range(50, "Age", 0, 120) {
        Ok(_) => println!("Age 50 is valid"),
        Err(e) => println!("Error: {}", e),
    }
    match validate_range(150, "Age", 0, 120) {
        Ok(_) => println!("Age 150 is valid"),
        Err(e) => println!("Error: {}", e),
    }

    // 5. Collection validation
    println!("
--- 5. Collection Validation ---");
    let items = vec![1, 2, 3, 4, 5];
    match validate_collection_size(&items, "Items", 1, 10) {
        Ok(_) => println!("Collection size is valid"),
        Err(e) => println!("Error: {}", e),
    }

    let empty: Vec<i32> = vec![];
    match validate_not_empty(&empty, "Items") {
        Ok(_) => println!("Collection is valid"),
        Err(e) => println!("Error: {}", e),
    }

    // 6. User validation
    println!("
--- 6. User Validation ---");
    let user = User {
        username: "john_doe".to_string(),
        email: "[email protected]".to_string(),
        age: 25,
    };
    match user.validate() {
        Ok(_) => println!("User is valid: {:?}", user),
        Err(errors) => {
            println!("User validation failed:");
            for e in errors {
                println!("  - {}", e);
            }
        },
    }

    // 7. Product validation
    println!("
--- 7. Product Validation ---");
    let product = Product {
        name: "Laptop".to_string(),
        price: 999.99,
        quantity: 10,
    };
    match product.validate() {
        Ok(_) => println!("Product is valid: {:?}", product),
        Err(errors) => {
            println!("Product validation failed:");
            for e in errors {
                println!("  - {}", e);
            }
        },
    }

    // 8. Validation builder
    println!("
--- 8. Validation Builder ---");
    let result = Validator::new(&"test value")
        .add_rule(|v| validate_length(v, "Field", 3, 20))
        .add_rule(|v| if v.chars().all(|c| c.is_alphanumeric()) {
            Ok(())
        } else {
            Err(ValidationError::InvalidFormat("Field".to_string()))
        })
        .validate();

    match result {
        Ok(_) => println!("Builder validation passed"),
        Err(errors) => {
            println!("Builder validation failed:");
            for e in errors {
                println!("  - {}", e);
            }
        },
    }

    // 9. URL validation
    println!("
--- 9. URL Validation ---");
    match validate_url("https://example.com") {
        Ok(_) => println!("URL is valid"),
        Err(e) => println!("Error: {}", e),
    }

    println!("
=== All Parameter Validation Examples Completed ===");
}

💻 Captura de Exceções rust

🟡 intermediate ⭐⭐⭐

Tratar erros usando Result, Option e tipos de erro personalizados

⏱️ 25 min 🏷️ rust, web, error handling
Prerequisites: Intermediate Rust
// Web Rust Exception Catching Examples
// Error handling with Result, Option, and custom errors

use std::fmt;
use std::io;

// 1. Custom Error Types

/// Custom error type with detailed information
#[derive(Debug)]
enum AppError {
    Io(io::Error),
    Parse(String),
    Validation(String),
    NotFound(String),
    Permission(String),
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AppError::Io(err) => write!(f, "IO error: {}", err),
            AppError::Parse(msg) => write!(f, "Parse error: {}", msg),
            AppError::Validation(msg) => write!(f, "Validation error: {}", msg),
            AppError::NotFound(msg) => write!(f, "Not found: {}", msg),
            AppError::Permission(msg) => write!(f, "Permission denied: {}", msg),
        }
    }
}

impl std::error::Error for AppError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            AppError::Io(err) => Some(err),
            _ => None,
        }
    }
}

impl From<io::Error> for AppError {
    fn from(err: io::Error) -> Self {
        AppError::Io(err)
    }
}

// 2. Result Handling

/// Basic Result handling
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Division by zero"))
    } else {
        Ok(a / b)
    }
}

/// Result with custom error
fn read_file_content(path: &str) -> Result<String, AppError> {
    std::fs::read_to_string(path).map_err(AppError::from)
}

/// Chain multiple Results
fn process_file(path: &str) -> Result<usize, AppError> {
    let content = read_file_content(path)?;
    let count = content.lines().count();
    Ok(count)
}

// 3. Option Handling

/// Safe division returning Option
fn safe_divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 {
        None
    } else {
        Some(a / b)
    }
}

/// Get element from vector
fn get_element<T>(vec: &[T], index: usize) -> Option<&T> {
    vec.get(index)
}

/// Parse to integer with Option
fn parse_optional(s: &str) -> Option<i32> {
    s.parse().ok()
}

// 4. Error Propagation

/// Function that propagates errors
fn load_config(path: &str) -> Result<String, AppError> {
    let content = std::fs::read_to_string(path)?;
    Ok(content)
}

/// Function with custom error creation
fn validate_username(name: &str) -> Result<(), AppError> {
    if name.is_empty() {
        return Err(AppError::Validation("Username cannot be empty".to_string()));
    }
    if name.len() < 3 {
        return Err(AppError::Validation("Username must be at least 3 characters".to_string()));
    }
    if name.len() > 20 {
        return Err(AppError::Validation("Username must be at most 20 characters".to_string()));
    }
    Ok(())
}

// 5. Multiple Error Types

/// Operation that can fail in multiple ways
fn complex_operation(path: &str) -> Result<String, Box<dyn std::error::Error>> {
    let content = std::fs::read_to_string(path)?;
    let trimmed = content.trim();
    if trimmed.is_empty() {
        return Err("File is empty".into());
    }
    Ok(trimmed.to_string())
}

// 6. Error Recovery

/// Attempt operation with fallback
fn read_with_fallback(path: &str, fallback: &str) -> String {
    read_file_content(path).unwrap_or_else(|_| fallback.to_string())
}

/// Try multiple alternatives
fn try_alternatives(paths: &[&str]) -> Result<String, AppError> {
    for path in paths {
        if let Ok(content) = read_file_content(path) {
            return Ok(content);
        }
    }
    Err(AppError::NotFound("No valid path found".to_string()))
}

// 7. Error Context

/// Add context to errors
fn read_file_with_context(path: &str) -> Result<String, AppError> {
    std::fs::read_to_string(path)
        .map_err(|e| AppError::Io(e))
        .and_then(|content| {
            if content.is_empty() {
                Err(AppError::Parse(format!("File {} is empty", path)))
            } else {
                Ok(content)
            }
        })
}

// 8. Panic Handling

/// Function that may panic
fn risky_operation(value: i32) -> i32 {
    if value < 0 {
        panic!("Negative value not allowed: {}", value);
    }
    value * 2
}

/// Catch panic using std::panic
fn safe_risky_operation(value: i32) -> Result<i32, String> {
    std::panic::catch_unwind(|| risky_operation(value))
        .map_err(|_| "Panic occurred".to_string())
}

// 9. Combining Results

/// Combine multiple Results
fn combine_results(results: Vec<Result<i32, String>>) -> Result<Vec<i32>, String> {
    results.into_iter().collect()
}

/// Collect first error or all successes
fn collect_first_error<T, E>(results: Vec<Result<T, E>>) -> Result<Vec<T>, E> {
    results.into_iter().collect()
}

// 10. Conditional Error Handling

/// Handle error based on type
fn handle_io_error(result: Result<String, io::Error>) -> String {
    match result {
        Ok(content) => content,
        Err(e) if e.kind() == io::ErrorKind::NotFound => "File not found".to_string(),
        Err(e) if e.kind() == io::ErrorKind::PermissionDenied => "Permission denied".to_string(),
        Err(e) => format!("IO error: {}", e),
    }
}

// Usage Examples
fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("=== Web Rust Exception Catching Examples ===\n");

    // 1. Basic Result handling
    println!("--- 1. Basic Result ---");
    match divide(10.0, 2.0) {
        Ok(result) => println!("10 / 2 = {}", result),
        Err(e) => println!("Error: {}", e),
    }
    match divide(10.0, 0.0) {
        Ok(result) => println!("10 / 0 = {}", result),
        Err(e) => println!("Error: {}", e),
    }

    // 2. Option handling
    println!("
--- 2. Option ---");
    println!("10 / 2 = {:?}", safe_divide(10.0, 2.0));
    println!("10 / 0 = {:?}", safe_divide(10.0, 0.0));

    let vec = vec![1, 2, 3];
    println!("Element at index 1: {:?}", get_element(&vec, 1));
    println!("Element at index 10: {:?}", get_element(&vec, 10));

    // 3. Custom errors
    println!("
--- 3. Custom Errors ---");
    match validate_username("") {
        Ok(_) => println!("Username valid"),
        Err(e) => println!("Validation error: {}", e),
    }
    match validate_username("ab") {
        Ok(_) => println!("Username valid"),
        Err(e) => println!("Validation error: {}", e),
    }
    match validate_username("valid_user") {
        Ok(_) => println!("Username valid"),
        Err(e) => println!("Validation error: {}", e),
    }

    // 4. Error propagation
    println!("
--- 4. Error Propagation ---");
    match load_config("nonexistent.txt") {
        Ok(_) => println!("Config loaded"),
        Err(e) => println!("Load error: {}", e),
    }

    // 5. Multiple error types
    println!("
--- 5. Multiple Error Types ---");
    match complex_operation("test.txt") {
        Ok(content) => println!("Content length: {}", content.len()),
        Err(e) => println!("Error: {}", e),
    }

    // 6. Fallback
    println!("
--- 6. Fallback ---");
    let result = read_with_fallback("missing.txt", "default content");
    println!("With fallback: {}", result);

    // 7. Try alternatives
    println!("
--- 7. Try Alternatives ---");
    let paths = ["file1.txt", "file2.txt", "file3.txt"];
    match try_alternatives(&paths) {
        Ok(_) => println!("Found a valid file"),
        Err(e) => println!("All alternatives failed: {}", e),
    }

    // 8. Combine results
    println!("
--- 8. Combine Results ---");
    let results = vec![
        Ok(1),
        Ok(2),
        Err("Error 3".to_string()),
        Ok(4),
    ];
    match combine_results(results) {
        Ok(values) => println!("All results: {:?}", values),
        Err(e) => println!("Failed with: {}", e),
    }

    // 9. Panic handling
    println!("
--- 9. Panic Handling ---");
    match safe_risky_operation(10) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => println!("Panic caught: {}", e),
    }
    match safe_risky_operation(-5) {
        Ok(result) => println!("Result: {}", result),
        Err(e) => println!("Panic caught: {}", e),
    }

    // 10. Conditional error handling
    println!("
--- 10. Conditional Error Handling ---");
    let io_result: Result<String, io::Error> = Err(io::Error::new(io::ErrorKind::NotFound, "Not found"));
    println!("IO error: {}", handle_io_error(io_result));

    println!("
=== All Exception Catching Examples Completed ===");
    Ok(())
}