armdb 0.12.0

sharded bitcask key-value storage optimized for NVMe
Documentation
//! fjall-backed `LsmTree` collection (feature `fjall`). Scales past RAM via a
//! sparse LSM index. Single-node v1 — no replication. See
//! `docs/superpowers/specs/26-06-20-lsm-tree-collection.md`.

mod config;
pub use config::{LsmConfig, LsmDbConfig, PersistPolicy};

#[cfg(feature = "armour")]
mod handler;

mod iter;
pub use iter::LsmIter;
pub(crate) use iter::{LsmKeyIter, TryLsmIter, TryLsmKeyIter};

pub mod shard;
pub use shard::LsmShard;

mod tree;
pub use tree::LsmTree;

use crate::error::{DbError, DbResult};

/// Map a fjall result into a `DbResult`. `fjall::Error::Io` wraps a real
/// `std::io::Error`, so it is threaded through to `DbError::Io` — callers that
/// match on `DbError::Io` (e.g. retry/backoff policy) then see LSM I/O failures
/// too, not just Bitcask/FixedStore ones. Every other fjall variant (`Storage`,
/// `Poisoned`, `Locked`, …) has no counterpart in `DbError` yet, so it stays
/// flattened to `DbError::Fjall(String)` via `Display`.
pub(crate) fn map_fjall<T>(r: fjall::Result<T>) -> DbResult<T> {
    r.map_err(|e| match e {
        fjall::Error::Io(io_err) => DbError::Io(io_err),
        other => DbError::Fjall(other.to_string()),
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn map_fjall_preserves_io_variant() {
        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "boom");
        let kind = io_err.kind();
        match map_fjall::<()>(Err(fjall::Error::Io(io_err))) {
            Err(DbError::Io(e)) => assert_eq!(e.kind(), kind),
            other => panic!("expected DbError::Io, got {other:?}"),
        }
    }

    #[test]
    fn map_fjall_flattens_other_variants_to_fjall_string() {
        match map_fjall::<()>(Err(fjall::Error::Locked)) {
            Err(DbError::Fjall(s)) => assert!(!s.is_empty()),
            other => panic!("expected DbError::Fjall, got {other:?}"),
        }
    }
}