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}