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. Opens both
11//!   `packages` databases from `spam db build` and `index` databases from
12//!   `spam index`.
13//!
14//! ## File format
15//!
16//! ```text
17//! # spam-db-v2\t{options|packages|index}\n
18//! [256 x 16-byte index entries: (offset: u64le, length: u64le)]
19//! [concatenated zstd-compressed bucket blobs]
20//! ```
21//!
22//! Each line is placed in every bucket for each unique byte in its search key.
23//! Queries decompress only the bucket for `query[0]`.
24//!
25//! ## Usage
26//!
27//! ```rust,no_run
28//! use spam_db::SpamDb;
29//!
30//! match SpamDb::open("files.db").unwrap() {
31//!     SpamDb::Options(db) => {
32//!         for rec in db.query("services.nginx").unwrap() {
33//!             println!("{}: {:?}", rec.name, rec.summary);
34//!         }
35//!     }
36//!     SpamDb::Packages(db) => {
37//!         for rec in db.query("/bin/").unwrap() {
38//!             println!("{} -> {}", rec.path, rec.packages.join(", "));
39//!         }
40//!     }
41//!     SpamDb::Index(db) => {
42//!         for rec in db.query("/bin/").unwrap() {
43//!             println!("{} -> {}", rec.path, rec.packages.join(", "));
44//!         }
45//!     }
46//! }
47//! ```
48//!
49//! ```rust,no_run
50//! use spam_db::OptionsDb;
51//!
52//! let db = OptionsDb::open("options.db").unwrap();
53//! let results = db.query("networking.firewall").unwrap();
54//! ```
55
56mod format;
57
58pub mod error;
59pub mod options;
60pub mod packages;
61
62pub use error::Error;
63pub use format::DbKind;
64pub use options::{OptionRecord, OptionsDb};
65pub use packages::{FileKind, FileRecord, PackagesDb};
66
67/// Convenience alias for `Result<T, spam_db::Error>`.
68pub type Result<T> = std::result::Result<T, Error>;
69
70/// A spam database returned by [`SpamDb::open`] when the kind is not known at
71/// compile time.
72#[derive(Debug)]
73pub enum SpamDb {
74  /// An options database (NixOS module options).
75  Options(OptionsDb),
76  /// A package-manifest database from `spam db build`.
77  Packages(PackagesDb),
78  /// An autonomous package index from `spam index`.
79  Index(PackagesDb),
80}
81
82impl SpamDb {
83  /// Open a spam database, detecting the kind from the file header.
84  pub fn open(path: impl AsRef<std::path::Path>) -> Result<Self> {
85    let db = format::DbFile::open(path)?;
86    match db.kind {
87      DbKind::Options => Ok(SpamDb::Options(OptionsDb::from_file(db))),
88      DbKind::Packages => Ok(SpamDb::Packages(PackagesDb::from_file(db))),
89      DbKind::Index => Ok(SpamDb::Index(PackagesDb::from_file(db))),
90    }
91  }
92
93  /// The kind of this database.
94  pub fn kind(&self) -> DbKind {
95    match self {
96      SpamDb::Options(_) => DbKind::Options,
97      SpamDb::Packages(_) => DbKind::Packages,
98      SpamDb::Index(_) => DbKind::Index,
99    }
100  }
101}