Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Error Handling

libmagic-rs uses Rust's Result type system for comprehensive, type-safe error handling.

Error Types

LibmagicError

The main error enum covers all library operations:

#![allow(unused)]
fn main() {
use thiserror::Error;

#[derive(Debug, Error)]
pub enum LibmagicError {
    #[error("Parse error at line {line}: {message}")]
    ParseError { line: usize, message: String },

    #[error("Evaluation error: {0}")]
    EvaluationError(String),

    #[error("IO error: {0}")]
    IoError(#[from] std::io::Error),

    #[error("Invalid magic file format: {0}")]
    InvalidFormat(String),
}
}

Result Type Alias

For convenience, the library provides a type alias:

#![allow(unused)]
fn main() {
pub type Result<T> = std::result::Result<T, LibmagicError>;
}

Error Handling Patterns

Basic Error Handling

#![allow(unused)]
fn main() {
use libmagic_rs::{MagicDatabase, LibmagicError};

match MagicDatabase::load_from_file("magic.db") {
    Ok(db) => {
        // Use the database
        println!("Loaded magic database successfully");
    }
    Err(e) => {
        eprintln!("Failed to load magic database: {}", e);
        return;
    }
}
}

Using the ? Operator

#![allow(unused)]
fn main() {
fn analyze_file(path: &str) -> Result<String> {
    let db = MagicDatabase::load_from_file("magic.db")?;
    let result = db.evaluate_file(path)?;
    Ok(result.description)
}
}

Matching Specific Errors

#![allow(unused)]
fn main() {
use libmagic_rs::LibmagicError;

match db.evaluate_file("example.bin") {
    Ok(result) => println!("File type: {}", result.description),
    Err(LibmagicError::IoError(e)) => {
        eprintln!("File access error: {}", e);
    }
    Err(LibmagicError::EvaluationError(msg)) => {
        eprintln!("Evaluation failed: {}", msg);
    }
    Err(e) => {
        eprintln!("Other error: {}", e);
    }
}
}

Error Context

Adding Context with map_err

#![allow(unused)]
fn main() {
use libmagic_rs::LibmagicError;

fn load_custom_magic(path: &str) -> Result<MagicDatabase> {
    MagicDatabase::load_from_file(path).map_err(|e| {
        LibmagicError::InvalidFormat(format!(
            "Failed to load custom magic file '{}': {}",
            path, e
        ))
    })
}
}

Using anyhow for Application Errors

use anyhow::{Context, Result};
use libmagic_rs::MagicDatabase;

fn main() -> Result<()> {
    let db = MagicDatabase::load_from_file("magic.db").context("Failed to load magic database")?;

    let result = db
        .evaluate_file("example.bin")
        .context("Failed to analyze file")?;

    println!("File type: {}", result.description);
    Ok(())
}

Error Recovery

Graceful Degradation

#![allow(unused)]
fn main() {
fn analyze_with_fallback(path: &str) -> String {
    match MagicDatabase::load_from_file("magic.db") {
        Ok(db) => match db.evaluate_file(path) {
            Ok(result) => result.description,
            Err(_) => "unknown file type".to_string(),
        },
        Err(_) => "magic database unavailable".to_string(),
    }
}
}

Retry Logic

#![allow(unused)]
fn main() {
use std::thread;
use std::time::Duration;

fn load_with_retry(path: &str, max_attempts: u32) -> Result<MagicDatabase> {
    let mut attempts = 0;

    loop {
        match MagicDatabase::load_from_file(path) {
            Ok(db) => return Ok(db),
            Err(e) if attempts < max_attempts => {
                attempts += 1;
                eprintln!("Attempt {} failed: {}", attempts, e);
                thread::sleep(Duration::from_millis(100));
            }
            Err(e) => return Err(e),
        }
    }
}
}

Best Practices

1. Use Specific Error Types

#![allow(unused)]
fn main() {
// Good: Specific error information
Err(LibmagicError::ParseError {
    line: 42,
    message: "Invalid offset specification".to_string()
})

// Avoid: Generic error messages
Err(LibmagicError::EvaluationError("something went wrong".to_string()))
}

2. Provide Context

#![allow(unused)]
fn main() {
// Good: Contextual error information
fn parse_magic_file(path: &Path) -> Result<Vec<MagicRule>> {
    std::fs::read_to_string(path)
        .map_err(|e| LibmagicError::IoError(e))
        .and_then(|content| parse_magic_string(&content))
}

// Better: Even more context
fn parse_magic_file(path: &Path) -> Result<Vec<MagicRule>> {
    let content = std::fs::read_to_string(path).map_err(|e| {
        LibmagicError::InvalidFormat(format!(
            "Cannot read magic file '{}': {}",
            path.display(),
            e
        ))
    })?;

    parse_magic_string(&content).map_err(|e| {
        LibmagicError::InvalidFormat(format!("Invalid magic file '{}': {}", path.display(), e))
    })
}
}

3. Handle Errors at the Right Level

// Library level: Return detailed errors
pub fn evaluate_file<P: AsRef<Path>>(&self, path: P) -> Result<EvaluationResult> {
    // Detailed error handling
}

// Application level: Handle user-facing concerns
fn main() {
    match analyze_file("example.bin") {
        Ok(description) => println!("{}", description),
        Err(e) => {
            eprintln!("Error: {}", e);
            std::process::exit(1);
        }
    }
}

4. Document Error Conditions

#![allow(unused)]
fn main() {
/// Evaluate magic rules against a file
///
/// # Errors
///
/// This function will return an error if:
/// - The file cannot be read (`IoError`)
/// - The file is too large for processing (`EvaluationError`)
/// - Rule evaluation encounters invalid data (`EvaluationError`)
///
/// # Examples
///
/// ```rust,no_run
/// use libmagic_rs::MagicDatabase;
///
/// let db = MagicDatabase::load_from_file("magic.db")?;
/// match db.evaluate_file("example.bin") {
///     Ok(result) => println!("Type: {}", result.description),
///     Err(e) => eprintln!("Error: {}", e),
/// }
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn evaluate_file<P: AsRef<Path>>(&self, path: P) -> Result<EvaluationResult> {
    // Implementation
}
}

Testing Error Conditions

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_missing_file_error() {
        let result = MagicDatabase::load_from_file("nonexistent.magic");
        assert!(result.is_err());

        match result {
            Err(LibmagicError::IoError(_)) => (), // Expected
            _ => panic!("Expected IoError for missing file"),
        }
    }

    #[test]
    fn test_invalid_magic_file() {
        let result = parse_magic_string("invalid syntax here");
        assert!(result.is_err());

        if let Err(LibmagicError::ParseError { line, message }) = result {
            assert_eq!(line, 1);
            assert!(message.contains("syntax"));
        } else {
            panic!("Expected ParseError for invalid syntax");
        }
    }
}
}

This comprehensive error handling approach ensures libmagic-rs provides clear, actionable error information while maintaining type safety and enabling robust error recovery strategies.