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