Skip to main content

spam_db/
lib.rs

1//! Parser and query library for [spam](https://github.com/feel-co/spam) databases.
2//!
3//! spam indexes Nix package closures and `nixosOptionsDoc` output into
4//! compressed, bucket-indexed databases. Use this crate to open those
5//! databases and run substring queries against them.
6//!
7//! ## Database kinds
8//!
9//! - [`OptionsDb`]: NixOS module options, keyed by option name.
10//! - [`PackagesDb`]: Nix store file paths, keyed by path.
11//!
12//! ## File format
13//!
14//! ```text
15//! # spam-db-v1\t{options|packages}\n
16//! [256 x 8-byte index entries: (offset: u32le, length: u32le)]
17//! [concatenated zstd-compressed bucket blobs]
18//! ```
19//!
20//! Each line is placed in every bucket for each unique byte in its search key.
21//! Queries decompress only the bucket for `query[0]`.
22//!
23//! ## Usage
24//!
25//! ```rust,no_run
26//! use spam_db::SpamDb;
27//!
28//! match SpamDb::open("files.db").unwrap() {
29//!     SpamDb::Options(db) => {
30//!         for rec in db.query("services.nginx").unwrap() {
31//!             println!("{}: {:?}", rec.name, rec.summary);
32//!         }
33//!     }
34//!     SpamDb::Packages(db) => {
35//!         for rec in db.query("/bin/").unwrap() {
36//!             println!("{} -> {}", rec.path, rec.packages.join(", "));
37//!         }
38//!     }
39//! }
40//! ```
41//!
42//! ```rust,no_run
43//! use spam_db::OptionsDb;
44//!
45//! let db = OptionsDb::open("options.db").unwrap();
46//! let results = db.query("networking.firewall").unwrap();
47//! ```
48
49mod format;
50
51pub mod error;
52pub mod options;
53pub mod packages;
54
55pub use error::Error;
56pub use format::DbKind;
57pub use options::{OptionRecord, OptionsDb};
58pub use packages::{FileKind, FileRecord, PackagesDb};
59
60/// Convenience alias for `Result<T, spam_db::Error>`.
61pub type Result<T> = std::result::Result<T, Error>;
62
63/// A spam database of either kind, returned by [`SpamDb::open`] when the kind
64/// is not known at compile time.
65#[derive(Debug)]
66pub enum SpamDb {
67  /// An options database (NixOS module options).
68  Options(OptionsDb),
69  /// A packages database (file path to package name mappings).
70  Packages(PackagesDb),
71}
72
73impl SpamDb {
74  /// Open a spam database, detecting the kind from the file header.
75  pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self> {
76    let db = format::DbFile::open(path)?;
77    match db.kind {
78      DbKind::Options => Ok(SpamDb::Options(OptionsDb::from_file(db))),
79      DbKind::Packages => Ok(SpamDb::Packages(PackagesDb::from_file(db))),
80    }
81  }
82
83  /// The kind of this database.
84  pub fn kind(&self) -> DbKind {
85    match self {
86      SpamDb::Options(_) => DbKind::Options,
87      SpamDb::Packages(_) => DbKind::Packages,
88    }
89  }
90}