You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a fluent builder API to MagicDatabase so callers can compose configuration and rule loading in a readable, chainable style — complementing the existing convenience constructors without breaking any current call sites.
Background & Motivation
The current MagicDatabase API provides four static constructors:
Constructor
Behaviour
with_builtin_rules()
Built-in compiled rules, default config
with_builtin_rules_and_config(config)
Built-in rules, custom config
load_from_file(path)
External magic file, default config
load_from_file_with_config(path, config)
External magic file, custom config
These work well for simple cases. However, as configuration options grow (timeouts, MIME-type toggling, depth limits, etc.) the call-site becomes unwieldy when callers need to compose multiple options before deciding on a rule source. A builder provides:
A natural left-to-right reading order: create → configure → load
Early Result propagation per step (invalid configs fail fast)
Extensibility — new builder methods can be added without new top-level constructors
Alignment with idiomatic Rust patterns used across the ecosystem (e.g. reqwest::ClientBuilder, tokio::runtime::Builder)
This issue tracks Core Flow 5 (Library API — Advanced Usage) from the Phase 1 spec and is part of Epic #24.
Proposed API
// Minimal usage — load from path, default configlet db = MagicDatabase::new().load("/usr/share/file/magic/Magdir/")?;// With explicit configurationlet db = MagicDatabase::new().with_config(EvaluationConfig::performance())?
.load("/usr/share/file/magic/Magdir/")?;// With built-in ruleslet db = MagicDatabase::new().with_config(config)?
.with_builtin_rules()?;
All existing constructors (load_from_file, load_from_file_with_config, with_builtin_rules, with_builtin_rules_and_config) are unchanged — they remain as convenience methods.
Implementation Plan
1. Introduce MagicDatabaseBuilder in src/lib.rs
/// Builder for `MagicDatabase` — use `MagicDatabase::new()` to obtain one.#[derive(Debug,Default)]pubstructMagicDatabaseBuilder{config:EvaluationConfig,}implMagicDatabaseBuilder{/// Apply and validate an `EvaluationConfig`.////// Returns `Err` immediately if the config fails validation/// (e.g. `timeout_ms` is `Some(0)`).pubfnwith_config(mutself,config:EvaluationConfig) -> Result<Self>{
config.validate()?;self.config = config;Ok(self)}/// Load magic rules from a file or directory and build the database.////// Delegates to `MagicDatabase::load_from_file_with_config`.pubfnload<P:AsRef<Path>>(self,path:P) -> Result<MagicDatabase>{MagicDatabase::load_from_file_with_config(path,self.config)}/// Build the database using built-in compiled rules.////// Delegates to `MagicDatabase::with_builtin_rules_and_config`.pubfnwith_builtin_rules(self) -> Result<MagicDatabase>{MagicDatabase::with_builtin_rules_and_config(self.config)}}
2. Add MagicDatabase::new()
implMagicDatabase{/// Create a new builder for `MagicDatabase`.////// # Examples////// ```rust,no_run/// use libmagic_rs::{MagicDatabase, EvaluationConfig};////// let db = MagicDatabase::new()/// .with_config(EvaluationConfig::performance())?/// .load("/usr/share/file/magic/Magdir/")?;/// # Ok::<(), Box<dyn std::error::Error>>(())/// ```#[must_use]pubfnnew() -> MagicDatabaseBuilder{MagicDatabaseBuilder::default()}// … existing methods unchanged …}
Summary
Add a fluent builder API to
MagicDatabaseso callers can compose configuration and rule loading in a readable, chainable style — complementing the existing convenience constructors without breaking any current call sites.Background & Motivation
The current
MagicDatabaseAPI provides four static constructors:with_builtin_rules()with_builtin_rules_and_config(config)load_from_file(path)load_from_file_with_config(path, config)These work well for simple cases. However, as configuration options grow (timeouts, MIME-type toggling, depth limits, etc.) the call-site becomes unwieldy when callers need to compose multiple options before deciding on a rule source. A builder provides:
Resultpropagation per step (invalid configs fail fast)reqwest::ClientBuilder,tokio::runtime::Builder)This issue tracks Core Flow 5 (Library API — Advanced Usage) from the Phase 1 spec and is part of Epic #24.
Proposed API
All existing constructors (
load_from_file,load_from_file_with_config,with_builtin_rules,with_builtin_rules_and_config) are unchanged — they remain as convenience methods.Implementation Plan
1. Introduce
MagicDatabaseBuilderinsrc/lib.rs2. Add
MagicDatabase::new()3. Unit tests (in
src/tests.rs)test_builder_default_config_load—MagicDatabase::new().load(path)succeedstest_builder_with_config—with_config(EvaluationConfig::performance())applies config correctlytest_builder_invalid_config_fails_fast— zero-ms timeout returnsErrfromwith_configtest_builder_with_builtin_rules—MagicDatabase::new().with_builtin_rules()matcheswith_builtin_rules()test_builder_chaining_is_equivalent_to_convenience_constructor— builder result equals static constructor resultAcceptance Criteria
MagicDatabase::new()returns aMagicDatabaseBuilderMagicDatabaseBuilder::with_config(config)validates config and returnsResult<Self>MagicDatabaseBuilder::load(path)loads rules and returnsResult<MagicDatabase>MagicDatabaseBuilder::with_builtin_rules()returnsResult<MagicDatabase>rustdocexamples#[deny(missing_docs)]remains satisfied (doc all new public items)Files to Modify
src/lib.rs— addMagicDatabaseBuilderstruct,MagicDatabase::new(), and builder methodssrc/tests.rs— add builder unit testsReferences
docs/plan/rust_libmagic_spec.mdEvaluationConfig::validate()—src/config.rs