nornir 0.4.3

Companion to cargo: dependency tracking, release gating, deploy, benchmarks, and documentation assembly. Project-agnostic.
Documentation
//! Warehouse — append-only columnar store backing nornir's bench/symbol
//! history.
//!
//! The canonical backend is [`iceberg::IcebergWarehouse`] (Apache Iceberg via
//! `iceberg-rust` over the single-file **nornir-catalog** redb catalogue).
//! Every derived fact is an Iceberg row keyed by git SHA. A `RemoteWarehouse`
//! (Arrow Flight to `nornir-server`, Phase 5) will reuse the same [`Warehouse`]
//! trait surface; the trait is the durable abstraction.

pub mod iceberg;
pub mod iceberg_schema;
pub mod dep_graph;
pub mod funnel;

use std::path::Path;

use anyhow::Result;
use uuid::Uuid;

use crate::bench::BenchRun;
use crate::config::Storage;

/// Storage backend for bench/symbol history. Sync API; the async
/// Flight server wraps a sync impl when it ships.
pub trait Warehouse: Send + Sync {
    fn append_bench_run(&self, repo: &str, run: &BenchRun) -> Result<Uuid>;
    fn query_bench_runs(&self, filter: &BenchFilter) -> Result<Vec<BenchRun>>;
}

#[derive(Debug, Default, Clone)]
pub struct BenchFilter {
    pub repo: Option<String>,
    pub machine: Option<String>,
    pub limit: Option<usize>,
}

impl BenchFilter {
    pub fn for_repo(repo: impl Into<String>) -> Self {
        Self { repo: Some(repo.into()), machine: None, limit: None }
    }
}

/// Open the configured warehouse. Returns a boxed trait object so
/// callers don't bake an impl into their signatures. All local storage
/// kinds resolve to the Iceberg backend; only `remote` is distinct.
pub fn open(storage: &Storage, workspace_root: &Path) -> Result<Box<dyn Warehouse>> {
    match storage.kind.as_str() {
        "" | "local" | "iceberg" => {
            let root = if storage.local_path.is_empty() {
                workspace_root.join("workspace_holger/.nornir/warehouse")
            } else {
                workspace_root.join(&storage.local_path).join("warehouse")
            };
            Ok(Box::new(iceberg::IcebergWarehouse::open(&root)?))
        }
        "remote" => anyhow::bail!("remote warehouse not yet implemented (Phase 5)"),
        other => anyhow::bail!("unknown storage.kind: {other}"),
    }
}