magic_db/
lib.rs

1#![forbid(unsafe_code)]
2#![deny(unused_imports)]
3#![deny(missing_docs)]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5//! # `magic-db`: Precompiled Magic Rules Database
6//!
7//! A precompiled database of file type detection rules based on the original `libmagic` project,
8//! optimized and adapted for use with [`pure-magic`](https://crates.io/crates/pure-magic).
9//! This crate provides ready-to-use file type detection capabilities without requiring external rule files.
10//!
11//! ## Features
12//!
13//! - **Precompiled Rules**: Optimized database embedded directly in your binary
14//! - **No External Dependencies**: All rules are included in the compiled crate
15//! - **Enhanced Rules**: Improved and extended versions of the original `libmagic` rules
16//! - **Easy Integration**: Simple one-line access to the compiled database
17//!
18//! ### Optional Cargo Features
19//!
20//! - **global**: Enables `magic_db::global()`, a lazily-initialized, process-wide `MagicDb`.
21//!   This provides a convenient singleton but is optional. If you need explicit lifetime
22//!   control or multiple independent instances, use `magic_db::load()` instead.
23//!
24//! ## Installation
25//!
26//! Add `magic-db` to your `Cargo.toml`:
27//!
28//! ```toml
29//! [dependencies]
30//! magic-db = "0.1"  # Replace with the latest version
31//! pure-magic = "0.1"  # Required peer dependency
32//! ```
33//!
34//! ## Usage
35//!
36//! ### Manual lifecycle (default)
37//!
38//! ```rust
39//! use std::fs::File;
40//! use std::env::current_exe;
41//!
42//! fn main() -> Result<(), pure_magic::Error> {
43//!     // Open the precompiled database
44//!     let db = magic_db::load()?;
45//!
46//!     // Use it to detect file types
47//!     let mut file = File::open(current_exe()?)?;
48//!     let magic = db.first_magic(&mut file, None)?;
49//!     assert!(!magic.is_default());
50//!
51//!     println!("File type: {}", magic.message());
52//!     println!("MIME type: {}", magic.mime_type());
53//!     Ok(())
54//! }
55//! ```
56//!
57//! ### Global singleton (optional)
58//!
59//! The crate provides a **convenience global database** via the
60//! `global` feature. This is process-wide, lazily initialized, and
61//! kept alive until program termination.
62//!
63//! Enable it in `Cargo.toml`:
64//!
65//! ```toml
66//! magic_db = { version = "0.1", features = ["global"] }
67//! ```
68//!
69//! Then use it like this:
70//!
71//! ```rust
72//! use magic_db::global;
73//!
74//! let db = global().unwrap();
75//! ```
76//!
77//! **Note:** Use the global feature only if you want a single, shared
78//! database. For multiple independent instances or explicit lifetime
79//! management, use `magic_db::load()`.
80//!
81//! ## About the Rules
82//!
83//! This database contains slightly modified versions of the original `libmagic` rules that are available
84//! in the [`src/magdir`](https://github.com/qjerome/magic-rs/tree/main/magic-db/src/magdir) directory of this repository.
85//!
86//! Some of the rules have been:
87//! - **Adapted**: Modified to work with the [`pure-magic`](https://crates.io/crates/pure-magic) parser
88//! - **Optimized**: Performance improvements for common file types
89//! - **Extended**: Additional rules were created
90//! - **Fixed**: Corrections to inaccurate or problematic original rules
91//!
92//! ## Rule Exclusions
93//!
94//! The database intentionally excludes the `der` rules (ASN.1/DER encoding rules) because:
95//! - The [`pure-magic`](https://crates.io/crates/pure-magic) parser doesn't support (yet) the specific DER test types
96//!   implemented in the original `libmagic`
97//!
98//! ## Source Rules
99//!
100//! The source magic rules are available in the repository at:
101//! [`src/magdir`](https://github.com/qjerome/magic-rs/tree/main/magic-db/src/magdir)
102//!
103//! You can:
104//! 1. Browse the rules to understand how file types are detected
105//! 2. Suggest improvements by opening issues or pull requests
106//! 3. Use these rules as a reference for creating your own custom rules
107//!
108//! ## License
109//!
110//! This project is licensed under the **GPL-3.0 License**.
111//!
112//! ## See Also
113//!
114//! - [`pure-magic`](https://crates.io/crates/pure-magic): The core file type detection library
115//! - [`magic-embed`](https://crates.io/crates/magic-embed): The macro used to create this database
116//! - [`magic`](https://www.man7.org/linux/man-pages/man4/magic.4.html): Expected magic rule format
117
118use magic_embed::magic_embed;
119use pure_magic::{Error, MagicDb};
120
121#[cfg(feature = "global")]
122use std::sync::OnceLock;
123
124#[cfg(feature = "global")]
125static DB: OnceLock<MagicDb> = OnceLock::new();
126
127#[magic_embed(include=["magdir"], exclude=["magdir/der"])]
128struct CompiledDb;
129
130#[cfg(feature = "global")]
131#[cfg_attr(docsrs, doc(cfg(feature = "global")))]
132/// Returns a process-wide read-only [`MagicDb`] initialized on first use.
133///
134/// This function is provided as a convenience for applications that
135/// want a shared database without managing its lifetime explicitly.
136/// The database is kept alive until program termination.
137///
138/// If you need explicit control over the database lifetime or want
139/// multiple independent instances, use [`load`] instead.
140pub fn global() -> Result<&'static MagicDb, Error> {
141    match DB.get() {
142        Some(db) => Ok(db),
143        None => {
144            let _ = DB.set(CompiledDb::open()?);
145            Ok(DB.wait())
146        }
147    }
148}
149
150#[inline(always)]
151/// Loads a [`MagicDb`] from the embedded, precompiled database.
152///
153/// This function constructs an owned [`MagicDb`] from data embedded
154/// at compile time. No file system access or runtime dependencies
155/// are involved.
156///
157/// Each call returns a new, independent instance.
158pub fn load() -> Result<MagicDb, Error> {
159    CompiledDb::open()
160}
161
162#[cfg(test)]
163mod test {
164    use crate as magic_db;
165    use std::{env, fs::File};
166
167    #[test]
168    fn test_compiled_db() {
169        let db = magic_db::load().unwrap();
170        let mut exe = File::open(env::current_exe().unwrap()).unwrap();
171        let magic = db.first_magic(&mut exe, None).unwrap();
172        println!("{}", magic.message());
173        assert!(!magic.is_default())
174    }
175
176    #[test]
177    #[cfg(feature = "global")]
178    fn test_compiled_db_static() {
179        let db = crate::global().unwrap();
180        let mut exe = File::open(env::current_exe().unwrap()).unwrap();
181        let magic = db.first_magic(&mut exe, None).unwrap();
182        println!("{}", magic.message());
183        assert!(!magic.is_default())
184    }
185}