# spam-db

Rust library for reading [spam](https://github.com/feel-co/spam) databases.

SPAM indexes Nix package closures and `nixosOptionsDoc` output into compressed,
bucket-indexed databases. This crate lets you open those databases and run
substring queries against them.

## Usage

```toml
[dependencies]
spam-db = "0.2"
```

### Query an options database

```rust
use spam_db::OptionsDb;

let db = OptionsDb::open("options.db")?;
for rec in db.query("services.nginx")? {
    println!("{}", rec.name);
    if let Some(summary) = rec.summary {
        println!("  {summary}");
    }
}
```

### Query a packages database

```rust
use spam_db::PackagesDb;

let db = PackagesDb::open("files.db")?;
for rec in db.query("/bin/")? {
    println!("{} -> {}", rec.path, rec.packages.join(", "));
}
```

### Auto-detect database kind

```rust
use spam_db::SpamDb;

match SpamDb::open("unknown.db")? {
    SpamDb::Options(db) => { /* ... */ }
    SpamDb::Packages(db) => { /* ... */ }
    SpamDb::Index(db) => { /* ... */ }
}
```

## Database format

A spam database is a binary file with three sections:

```plaintext
# spam-db-v2\t{options|packages|index}\n
[256 x 16-byte index entries: (offset: u64le, length: u64le)]
[concatenated zstd-compressed bucket blobs]
```

Each line in the database is placed in every bucket corresponding to a unique
byte in its search key. Queries decompress only the bucket for `query[0]`,
keeping lookup sublinear in the total database size.

The `packages` kind is produced by `spam db build` from local package manifests.
The `index` kind is produced by `spam index` from nixpkgs and binary-cache file
listings. `PackagesDb` can query both kinds, but consumers can distinguish them
with `SpamDb::kind()`.

## Building spam databases

Use the [spam CLI](https://github.com/feel-co/spam):

```bash
spam db build --manifest packages.json --output files.db
spam db build --manifest options.json --output options.db
```
