reddb-io-server 1.25.1

RedDB server-side engine: storage, runtime, replication, MCP, AI, and the gRPC/HTTP/RedWire/PG-wire dispatchers. Re-exported by the umbrella `reddb` crate.
Documentation
//! Validated keyed reads shared by runtime and application ports.
//!
//! Callers choose their existing validation policy at construction. Only typed
//! owned results leave this module; projection callbacks cannot be supplied by
//! callers or re-enter storage while the segment read guard is held.

use crate::catalog::CollectionModel;
use crate::storage::{EntityId, UnifiedEntity};
use crate::{RedDBError, RedDBResult, RedDBRuntime};
use reddb_types::Value;

pub(crate) struct KvReadRecord {
    pub(crate) id: EntityId,
    pub(crate) created_at: u64,
    pub(crate) updated_at: u64,
    pub(crate) value: Option<Value>,
}

pub(crate) struct KvReadResolver<'a> {
    runtime: &'a RedDBRuntime,
    model: CollectionModel,
    collection: &'a str,
}

impl<'a> KvReadResolver<'a> {
    pub(crate) fn for_keyed_model(
        runtime: &'a RedDBRuntime,
        model: CollectionModel,
        collection: &'a str,
    ) -> RedDBResult<Self> {
        if let Some(contract) = runtime.db().collection_contract_arc(collection) {
            if contract.declared_model != model && contract.declared_model != CollectionModel::Mixed
            {
                return Err(RedDBError::InvalidOperation(format!(
                    "collection '{}' is declared as '{}' and does not allow '{}' operations",
                    collection,
                    super::impl_kv::keyed_model_name(contract.declared_model),
                    super::impl_kv::keyed_model_name(model)
                )));
            }
        }
        Ok(Self {
            runtime,
            model,
            collection,
        })
    }

    pub(crate) fn for_rest_kv(runtime: &'a RedDBRuntime, collection: &'a str) -> RedDBResult<Self> {
        crate::application::collection_contract_enforcer::ensure_collection_model_read(
            &runtime.db(),
            collection,
            CollectionModel::Kv,
        )?;
        Ok(Self {
            runtime,
            model: CollectionModel::Kv,
            collection,
        })
    }

    pub(crate) fn record(&self, key: &str) -> Option<KvReadRecord> {
        self.project(key, |entity| KvReadRecord {
            id: entity.id,
            created_at: entity.created_at,
            updated_at: entity.updated_at,
            value: super::impl_kv::kv_value_from_entity(entity),
        })
    }

    pub(super) fn owned_entity(&self, key: &str) -> Option<UnifiedEntity> {
        self.project(key, Clone::clone)
    }

    fn project<T>(&self, key: &str, projection: impl FnMut(&UnifiedEntity) -> T) -> Option<T> {
        let store = self.runtime.db().store();
        let manager = store.get_collection(self.collection)?;
        let versioned = is_versioned(self.runtime, self.model, self.collection);
        let snapshot = if versioned {
            super::impl_core::capture_current_snapshot()
        } else {
            None
        };
        manager.project_key_entity(key, snapshot.as_ref(), versioned, projection)
    }
}

pub(super) fn is_versioned(
    runtime: &RedDBRuntime,
    model: CollectionModel,
    collection: &str,
) -> bool {
    model == CollectionModel::Kv && runtime.vcs_is_versioned(collection).unwrap_or(false)
}

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

    #[test]
    fn kv_read_resolver_requires_validation_and_preserves_legacy_policy_difference() {
        let runtime = RedDBRuntime::in_memory().expect("runtime");
        runtime
            .execute_query("KV PUT shared.item = 7")
            .expect("seed");
        let mut contract = runtime
            .db()
            .collection_contract("shared")
            .expect("contract");
        contract.declared_model = CollectionModel::Table;
        runtime
            .db()
            .save_collection_contract(contract.clone())
            .expect("implicit table");
        assert!(KvReadResolver::for_keyed_model(&runtime, CollectionModel::Kv, "shared").is_err());
        assert!(KvReadResolver::for_rest_kv(&runtime, "shared")
            .expect("legacy REST read")
            .record("item")
            .is_some());
        contract.origin = crate::physical::ContractOrigin::Explicit;
        runtime
            .db()
            .save_collection_contract(contract)
            .expect("explicit table");
        assert!(KvReadResolver::for_rest_kv(&runtime, "shared").is_err());
    }
}