# Embedded API In an application's Cargo.toml, use path dependencies pointing to this checkout: ```toml [dependencies] ratdb-core = { path = "/path/to/ratdb/crates/ratdb-core" } ratdb-document = { path = "/path/to/ratdb/crates/ratdb-document" } serde_json = "1" ``` ```rust use ratdb_core::{file::FileStorage, Config, Database}; use ratdb_document::Documents; use serde_json::json; fn main() -> Result<(), Box> { let core = Database::open(FileStorage::open("shiny.ratdb")?, Config::default())?; if core.suspect_headers() { return Err("suspect header: investigate recovery".into()); } let mut db = Documents(core); db.put("cars", "car:001", json!({"make":"Ford", "year":2021}))?; assert_eq!(db.get("cars", "car:001")?.unwrap()["make"], "Ford"); db.scan("cars", Some(("make", &json!("Ford"))), 100, 10_000, |document| println!("{document}"))?; db.delete("cars", "car:001")?; Ok(()) } ``` Core `Database` offers `open`, `get`, `put`, `delete`, `scan`, `stats`, `suspect_headers`, and `into_storage`. `stats` is (record_count, live_record_bytes, generation). Scan callbacks receive borrowed bytes and return false to stop. `get` returns an owned optional value. Mutations require mutable access. Single-key put/delete and sorted multi-key apply share the same commit protocol. Empty keys are invalid; empty values and arbitrary binary data are valid. `Documents` offers upsert/replace `put`, `get`, `delete`, `scan`, and `collections`. Scans emit owned JSON values; a caller that retains all results can use unbounded memory. Callbacks execute synchronously. No network authentication or runtime is involved. All core failures use `ratdb_core::Error`; errors distinguish invalid input, full storage, corruption, unsupported version, I/O/lock failure and poisoned state. I/O errors currently lose platform-specific detail. An I/O/full error during commit requires reopen. Library APIs are experimental and not published to a registry. ## Warrens, dens and shinies The conventional equivalents are logical databases, collections and documents. Legacy `put/get/delete/scan/collections` continue operating on legacy keys in the `default` warren. Use `put_in(warren, den, id, value)`, `get_in(warren, den, id)` and `delete_in(warren, den, id)` for explicit namespaces. `scan_in(warren, den, filter, callback)` seeks to the den and examines at most 10,000 candidates and emits at most 100 matches, returning `(examined, possibly_limited)`. Callback retention remains the caller's responsibility. `names(None,None)` lists warrens; `names(Some(warren),None)` lists dens; `names(Some(warren),Some(den))` lists IDs only. It returns `(names, truncated)`, capped at 100 distinct names and 10,000 examined records. Use `page_in` for document pagination. `create_namespace(w, None)` persists an empty warren, and `create_namespace(w, Some(d))` persists both parent and den in one commit. Repeated creates are idempotent. `drop_empty_namespace` returns Dropped, Missing or NotEmpty. Its emptiness check seeks directly to child key ranges independently of listing limits. ```rust // With `db: Documents`: // db.put_in("garage", "cars", "car:001", json!({"make":"Ford"}))?; // let shiny = db.get_in("garage", "cars", "car:001")?; ``` The core does not know these terms. Its persistence and no_std boundaries are unchanged. ## Single-document edits and cursor pages Use `insert_in(w, d, id, value)` to reserve an ID within a den. It returns false if that ID already exists. `put_in` can replace the entire shiny. `update_in(w, d, id, fields)` modifies an existing shiny and returns false when it is missing. Supply a nonempty object of top-level fields. Each supplied field replaces its entire previous value, including nested objects. JSON null is stored as a value. The ID is immutable. Successful insert and update perform one storage commit while the caller holds exclusive mutable access. The server holds its database mutex through the read and the commit. `page_in(w, d, after, limit, filter)` returns documents ordered by UTF-8 ID bytes. Start with `None` and pass the returned `next_after` unchanged until it is `None`. Pass the same warren, den and filter each time. A page holds up to 1–100 requested matches and 512 KiB of serialized document contents. A call examines up to 10,000 candidates after locating the start, plus range-boundary lookahead. If the examination budget expires before a match, the page may be empty with a continuation. Seeking incurs extra reads in the sparse index and snapshot. A document exceeding the entire page budget returns a capacity error. RAM for parsed JSON, framing, cursors and temporary values is additional. A page sees one committed state. Between calls, edits are visible. IDs inserted at or behind the cursor are skipped, and deletions ahead of it disappear. Deleting the cursor record does not prevent continuation. The server releases the mutex between pages. A cursor contains an ID and confers no access privileges. IDs are the only unique field. Use insert with a normalized username as the ID if uniqueness by username is required. Relationships and foreign keys are application-managed. Individual server commands do not form a multi-document transaction. Embedded callers can use [write_batch](EMBEDDED_DOCUMENTS.md). Ordinary JSON objects, strings, booleans, arrays and null are supported. Integers fit signed 64-bit or unsigned 64-bit range exactly. Fractional numbers and larger integer literals may be represented as finite binary64 values with rounding. For exact monetary decimals or larger integers use strings or scaled integers within range. `Documents::check()` scans the committed snapshot in key order. It verifies the core record checksums and ordering, then checks UTF-8 namespace encodings, JSON objects with matching string IDs, and catalog markers (including the explicit parent of each explicit den). The shell and direct-file `check` and the backup/restore source check use these rules. Core-only applications using arbitrary binary values should use `Database::scan` for a complete physical integrity traversal. Checks cover the active snapshot and perform no repairs.