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

Code Style

libmagic-rs follows strict code style guidelines to ensure consistency, readability, and maintainability across the codebase.

Formatting

Rustfmt Configuration

The project uses rustfmt with default settings. All code must be formatted before committing:

# Format all code
cargo fmt

# Check formatting without changing files
cargo fmt -- --check

Key Formatting Rules

  • Line length: 100 characters (rustfmt default)
  • Indentation: 4 spaces (no tabs)
  • Trailing commas: Required in multi-line constructs
  • Import organization: Automatic grouping and sorting
#![allow(unused)]
fn main() {
// Good: Proper formatting
use std::collections::HashMap;
use std::path::Path;

use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::parser::ast::MagicRule;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvaluationResult {
    pub description: String,
    pub mime_type: Option<String>,
    pub confidence: f64,
}
}

Naming Conventions

Types and Structs

Use PascalCase for types, structs, enums, and traits:

#![allow(unused)]
fn main() {
// Good
pub struct MagicDatabase {}
pub enum OffsetSpec {}
pub trait BinaryRegex {}

// Bad
pub struct magic_database {}
pub enum offset_spec {}
}

Functions and Variables

Use snake_case for functions, methods, and variables:

#![allow(unused)]
fn main() {
// Good
pub fn parse_magic_file(path: &Path) -> Result<Vec<MagicRule>> { }
let magic_rules = vec![];
let file_buffer = FileBuffer::new(path)?;

// Bad
pub fn ParseMagicFile(path: &Path) -> Result<Vec<MagicRule>> { }
let magicRules = vec![];
}

Constants

Use SCREAMING_SNAKE_CASE for constants:

#![allow(unused)]
fn main() {
// Good
const DEFAULT_BUFFER_SIZE: usize = 8192;
const MAX_RECURSION_DEPTH: u32 = 50;

// Bad
const default_buffer_size: usize = 8192;
const maxRecursionDepth: u32 = 50;
}

Modules

Use snake_case for module names:

#![allow(unused)]
fn main() {
// Good
mod file_evaluator;
mod magic_parser;
mod output_formatter;

// Bad
mod MagicParser;
mod fileEvaluator;
}

Documentation Standards

Public API Documentation

All public items must have rustdoc comments with examples:

#![allow(unused)]
fn main() {
/// Parses a magic file into a vector of magic rules
///
/// This function reads a magic file from the specified path and parses it into
/// a collection of `MagicRule` structures that can be used for file type detection.
///
/// # Arguments
///
/// * `path` - Path to the magic file to parse
///
/// # Returns
///
/// Returns `Ok(Vec<MagicRule>)` on success, or `Err(LibmagicError)` if parsing fails.
///
/// # Errors
///
/// This function will return an error if:
/// - The file cannot be read due to permissions or missing file
/// - The magic file contains invalid syntax
/// - Memory allocation fails during parsing
///
/// # Examples
///
/// ```rust,no_run
/// use libmagic_rs::parser::parse_magic_file;
///
/// let rules = parse_magic_file("magic.db")?;
/// println!("Loaded {} magic rules", rules.len());
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn parse_magic_file<P: AsRef<Path>>(path: P) -> Result<Vec<MagicRule>> {
    // Implementation
}
}

Module Documentation

Each module should have comprehensive documentation:

#![allow(unused)]
fn main() {
//! Magic file parser module
//!
//! This module handles parsing of magic files into an Abstract Syntax Tree (AST)
//! that can be evaluated against file buffers for type identification.
//!
//! The parser uses nom combinators for robust, efficient parsing with good
//! error reporting. It supports the standard magic file format with extensions
//! for modern file types.
//!
//! # Examples
//!
//! ```rust,no_run
//! use libmagic_rs::parser::parse_magic_file;
//!
//! let rules = parse_magic_file("magic.db")?;
//! for rule in &rules {
//!     println!("Rule: {}", rule.message);
//! }
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
}

Inline Comments

Use inline comments sparingly, focusing on why rather than what:

#![allow(unused)]
fn main() {
// Good: Explains reasoning
// Use indirect offset to handle relocatable executables
let actual_offset = resolve_indirect_offset(base_offset, buffer)?;

// Bad: States the obvious
// Set the offset to the resolved value
let actual_offset = resolved_offset;
}

Error Handling Style

Use Result Types

Always use Result for fallible operations:

#![allow(unused)]
fn main() {
// Good
pub fn parse_offset(input: &str) -> Result<OffsetSpec> {
    // Implementation that can fail
}

// Bad: Using Option for errors
pub fn parse_offset(input: &str) -> Option<OffsetSpec> {
    // Loses error information
}

// Bad: Using panics
pub fn parse_offset(input: &str) -> OffsetSpec {
    // Implementation that panics on error
    input.parse().unwrap()
}
}

Descriptive Error Messages

Provide context in error messages:

#![allow(unused)]
fn main() {
// Good: Specific, actionable error
return Err(LibmagicError::ParseError {
    line: line_number,
    message: format!("Invalid offset '{}': expected number or hex value", input),
});

// Bad: Generic error
return Err(LibmagicError::ParseError {
    line: line_number,
    message: "parse error".to_string(),
});
}

Error Propagation

Use the ? operator for error propagation:

#![allow(unused)]
fn main() {
// Good
pub fn load_and_parse(path: &Path) -> Result<Vec<MagicRule>> {
    let content = std::fs::read_to_string(path)?;
    let rules = parse_magic_string(&content)?;
    Ok(rules)
}

// Avoid: Manual error handling when ? works
pub fn load_and_parse(path: &Path) -> Result<Vec<MagicRule>> {
    let content = match std::fs::read_to_string(path) {
        Ok(content) => content,
        Err(e) => return Err(LibmagicError::IoError(e)),
    };
    // ...
}
}

Code Organization

Import Organization

Group imports in this order:

  1. Standard library
  2. External crates
  3. Internal crates/modules
#![allow(unused)]
fn main() {
// Standard library
use std::collections::HashMap;
use std::path::Path;

// External crates
use nom::{IResult, bytes::complete::tag};
use serde::{Deserialize, Serialize};
use thiserror::Error;

// Internal modules
use crate::evaluator::EvaluationContext;
use crate::parser::ast::{MagicRule, OffsetSpec};
}

Function Organization

Organize functions logically within modules:

#![allow(unused)]
fn main() {
impl MagicRule {
    // Constructors first
    pub fn new(/* ... */) -> Self {}

    // Public methods
    pub fn evaluate(&self, buffer: &[u8]) -> Result<bool> {}
    pub fn message(&self) -> &str {}

    // Private helpers last
    fn validate_offset(&self) -> bool {}
}
}

File Organization

Keep files focused and reasonably sized (< 500-600 lines):

#![allow(unused)]
fn main() {
// Good: Focused module
// src/parser/offset.rs - Only offset parsing logic

// Bad: Everything in one file
// src/parser/mod.rs - All parsing logic (thousands of lines)
}

Testing Style

Test Organization

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

    // Group related tests
    mod offset_parsing {
        use super::*;

        #[test]
        fn test_absolute_offset() {
            // Test implementation
        }

        #[test]
        fn test_indirect_offset() {
            // Test implementation
        }
    }

    mod error_handling {
        use super::*;

        #[test]
        fn test_invalid_syntax_error() {
            // Test implementation
        }
    }
}
}

Test Naming

Use descriptive test names that explain the scenario:

#![allow(unused)]
fn main() {
// Good: Descriptive names
#[test]
fn test_parse_absolute_offset_with_hex_value() {}

#[test]
fn test_parse_offset_returns_error_for_invalid_syntax() {}

// Bad: Generic names
#[test]
fn test_parse_offset() {}

#[test]
fn test_error() {}
}

Assertion Style

Use specific assertions with helpful messages:

#![allow(unused)]
fn main() {
// Good: Specific assertion with context
assert_eq!(
    result.unwrap().message,
    "ELF executable",
    "Magic rule should identify ELF files correctly"
);

// Good: Pattern matching for complex types
match result {
    Ok(OffsetSpec::Absolute(offset)) => assert_eq!(offset, 42),
    _ => panic!("Expected absolute offset with value 42"),
}

// Avoid: Generic assertions
assert!(result.is_ok());
}

Performance Considerations

Prefer Borrowing

Use references instead of owned values when possible:

#![allow(unused)]
fn main() {
// Good: Borrowing
pub fn evaluate_rule(rule: &MagicRule, buffer: &[u8]) -> Result<bool> {}

// Avoid: Unnecessary ownership
pub fn evaluate_rule(rule: MagicRule, buffer: Vec<u8>) -> Result<bool> {}
}

Avoid Unnecessary Allocations

#![allow(unused)]
fn main() {
// Good: String slice
pub fn parse_message(input: &str) -> &str {
    input.trim()
}

// Avoid: Unnecessary allocation
pub fn parse_message(input: &str) -> String {
    input.trim().to_string()
}
}

Use Appropriate Data Structures

#![allow(unused)]
fn main() {
// Good: Vec for ordered data
let rules: Vec<MagicRule> = parse_rules(input)?;

// Good: HashMap for key-value lookups
let mime_types: HashMap<String, String> = load_mime_mappings()?;

// Consider: BTreeMap for sorted keys
let sorted_rules: BTreeMap<u32, MagicRule> = rules_by_priority();
}

This style guide ensures consistent, readable, and maintainable code across the libmagic-rs project. All contributors should follow these guidelines, and automated tools enforce many of these rules during CI.