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
// 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:
// 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:
// 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:
// 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:
// 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:
/// 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:
//! 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:
// 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:
// 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:
// 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:
// 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:
- Standard library
- External crates
- Internal crates/modules
// 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:
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):
// 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#
#[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:
// 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:
// 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:
// 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#
// 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#
// 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.