| name | api-design |
| description | Rust library API design patterns including builder pattern, error handling, trait design, type safety, and CLI design with clap. Use when this capability is needed. |
| metadata | {"author":"evilbit-labs"} |
API Design Patterns (Rust Library & CLI)
When to Activate
- Designing or modifying public library API in
lib.rs
- Adding new public types, traits, or functions
- Reviewing API ergonomics and consistency
- Designing CLI arguments and output formats
- Planning breaking vs non-breaking changes
Library API Design
Builder Pattern
pub struct EvaluationConfig {
timeout: Duration,
max_rules: usize,
follow_symlinks: bool,
}
impl EvaluationConfig {
pub fn builder() -> EvaluationConfigBuilder {
EvaluationConfigBuilder::default()
}
}
pub struct EvaluationConfigBuilder { }
impl EvaluationConfigBuilder {
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn build(self) -> Result<EvaluationConfig, ConfigError> {
Ok(EvaluationConfig { })
}
}
Error Design
Three-Tier Error Hierarchy
pub enum LibmagicError {
Parse(ParseError),
Evaluation(EvaluationError),
Config(ConfigError),
Io(std::io::Error),
}
pub enum ParseError {
InvalidSyntax { line: usize, reason: String },
IoError(String),
}
impl std::fmt::Display for LibmagicError { }
impl std::error::Error for LibmagicError { }
Error Guidelines
- Use
thiserror for deriving Error implementations
- Errors should be actionable (include line numbers, context)
- Never expose internal paths or system details in public errors
- Implement
From conversions for ergonomic ? usage
Type Safety
Newtype Pattern
pub struct Offset(i64);
pub struct Score(u32);
pub struct Level(u32);
fn evaluate_at(offset: Offset, buffer: &[u8]) -> Result<Score, EvaluationError>;
Enum-Based Type Discrimination
pub enum OffsetSpec {
Absolute(i64),
Indirect { base: i64, pointer_type: TypeKind },
Relative(i64),
FromEnd(i64),
}
Public API Surface
Minimize Exposure
pub use crate::evaluator::EvaluationResult;
pub use crate::parser::MagicRule;
pub(crate) use crate::evaluator::EvaluationContext;
Document Everything Public
pub fn evaluate_buffer(&self, buffer: &[u8]) -> Result<Option<EvaluationResult>, EvaluationError>;
Trait Design
pub trait SafeBufferAccess {
fn get_byte(&self, offset: usize) -> Option<u8>;
fn get_slice(&self, offset: usize, len: usize) -> Option<&[u8]>;
fn len(&self) -> usize;
}
impl SafeBufferAccess for FileBuffer { }
impl SafeBufferAccess for &[u8] { }
CLI Design (clap)
Argument Structure
#[derive(Parser)]
#[command(name = "rmagic", about = "Identify file types")]
struct Args {
#[arg(required = true)]
files: Vec<PathBuf>,
#[arg(long)]
json: bool,
#[arg(long, value_name = "FILE")]
magic_file: Option<PathBuf>,
}
CLI Conventions
- Follow GNU
file command conventions where possible
- Short flags for common options (
-j for JSON)
- Long flags for all options (
--json)
- Positional arguments for files
-- to separate flags from file arguments
- Exit code 0 for success, 1 for errors
Output Format Consistency
- Text output:
filename: description (matches GNU file)
- JSON output: structured with
filename, matches, metadata
- Errors to stderr, results to stdout
- Quiet mode suppresses non-essential output
API Evolution
Non-Breaking Changes (patch/minor version)
- Adding new enum variants (if
#[non_exhaustive])
- Adding new optional fields to builders
- Adding new methods to existing types
- Loosening input constraints
Breaking Changes (major version)
- Removing or renaming public types/functions
- Changing function signatures
- Adding required fields to structs
- Tightening input constraints
- Changing error types
Defensive Techniques
#[non_exhaustive]
pub enum TypeKind {
Byte,
Short { endian: Endianness, signed: bool },
Long { endian: Endianness, signed: bool },
String { max_length: Option<usize> },
}
API Review Checklist
Before exposing new public API:
Converted and distributed by TomeVault — claim your Tome and manage your conversions.