obj-db 1.1.2

Embedded document database. Stable file format, full ACID, single-file portability.
Documentation
//! M11 #94 — design.md § Portability example, exit-gate integration.
//!
//! Mirrors the verbatim snippet under § Portability ("Copyable at
//! rest"), specifically the `Db::collection::<T>(name)` accessor.
//! design.md reads:
//!
//! ```text
//! db.attach("archive.obj", "archive")?;
//! let archived: Vec<Order> = db
//!     .collection::<Order>("archive.orders")
//!     .all()?
//!     .collect();
//! ```
//!
//! As with `design_md_queries::all_orders_iterates_every_doc`, the
//! M11 surface ships `Collection::all` as an owned `Vec<(Id, T)>`
//! shape (a streaming-iterator follow-up is documented elsewhere);
//! the test adapts the `.collect()` step to thread off the `Id` part
//! exactly as a user would. The collection name and `Db::collection`
//! call match design.md verbatim.

#![forbid(unsafe_code)]

use obj::{Db, Document};
use serde::{Deserialize, Serialize};
use tempfile::TempDir;

/// Design.md's `Order` type — fields trimmed to what the portability
/// example references.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct Order {
    customer_id: u64,
    total_cents: u64,
}

impl Document for Order {
    const COLLECTION: &'static str = "orders";
    const VERSION: u32 = 1;
}

/// Exit-gate: the design.md § Portability snippet compiles AND
/// returns the archived documents.
#[test]
fn design_md_attach_collection_reads_archived_orders() {
    let dir = TempDir::new().expect("tmp");
    let main_path = dir.path().join("main.obj");
    let archive_path = dir.path().join("archive.obj");

    // Populate the archive database (the file the design.md
    // snippet's `.attach` references).
    {
        let archive_db = Db::open(&archive_path).expect("open archive");
        archive_db
            .insert(Order {
                customer_id: 1,
                total_cents: 100,
            })
            .expect("seed archive 1");
        archive_db
            .insert(Order {
                customer_id: 2,
                total_cents: 200,
            })
            .expect("seed archive 2");
    }

    // Open the main db and attach the archive — matches the
    // design.md snippet line for line.
    let mut db = Db::open(&main_path).expect("open main");
    db.attach(&archive_path, "archive").expect("attach");

    // The design.md snippet:
    //
    //   let archived: Vec<Order> = db
    //       .collection::<Order>("archive.orders")
    //       .all()?
    //       .collect();
    //
    // M11 #94 ships `Collection::all` as `Result<Vec<(Id, T)>>`;
    // the `.collect()` step strips the `Id` pair off, matching what
    // a user would write today. The `Db::collection::<T>(name)`
    // call shape is verbatim.
    let archived: Vec<Order> = db
        .collection::<Order>("archive.orders")
        .all()
        .expect("all on archive.orders")
        .into_iter()
        .map(|(_id, doc)| doc)
        .collect();
    assert_eq!(archived.len(), 2);
    let totals: Vec<u64> = archived.iter().map(|o| o.total_cents).collect();
    assert!(totals.contains(&100));
    assert!(totals.contains(&200));
}