hyphae 3.0.0

Reactive cells and runtime primitives for rship
Documentation
//! [`MapQuery`] and [`CompileQuery`] implementations for reactive-map sources.
//!
//! [`CellMap`] and [`NestedMap`] are both valid query roots. Shared plan
//! properties are implemented through [`ReactiveMap`], while each source has
//! its own `MapQuery<Key = K, Value = V>` implementation so materialization can
//! be specialized where a no-op is sound.
//!
//! For [`CellMap`], `materialize` is a marker flip (same `Arc<inner>`, new
//! `PhantomData<CellImmutable>`) — there is no point allocating a fresh
//! cell map + forwarding subscription when the upstream is already a
//! cached, multicast cell map. Concrete plan-node structs (`InnerJoinPlan`,
//! ...) provide their own `MapQuery` impls and inherit the default
//! `materialize`.

use std::{hash::Hash, marker::PhantomData, sync::Arc};

use super::properties::{ByMapKey, ExactlyOne, PlanProperties};
use super::{BuildQueryRuntime, MapQuery};
use crate::{
    cell::CellImmutable,
    cell_map::CellMap,
    nested_map::NestedMap,
    subscription::SubscriptionGuard,
    traits::{CellValue, reactive_map::ReactiveMap},
};

fn install_reactive_source<M>(
    source: &M,
    identity: super::compiler::SourceIdentity,
    cx: &mut super::compiler::CompileContext,
    sink: crate::map_query::BoxedMapDiffSink<M::Key, M::Value>,
) -> Vec<SubscriptionGuard>
where
    M: ReactiveMap + Clone,
    M::Key: CellValue + Hash + Eq,
    M::Value: CellValue,
{
    cx.register_root(source, identity, sink);
    Vec::new()
}

impl<K, V, M> BuildQueryRuntime<K, V> for CellMap<K, V, M>
where
    K: CellValue + Hash + Eq,
    V: CellValue,
    M: Clone + Send + Sync + 'static,
{
    fn build_into(
        self,
        cx: &mut super::compiler::CompileContext,
        sink: crate::map_query::BoxedMapDiffSink<K, V>,
    ) -> Vec<SubscriptionGuard> {
        let identity = super::compiler::SourceIdentity::from_ptr(Arc::as_ptr(&self.inner));
        install_reactive_source(&self, identity, cx, sink)
    }

    fn raw_source_identity(&self) -> Option<super::compiler::SourceIdentity> {
        Some(super::compiler::SourceIdentity::from_ptr(Arc::as_ptr(
            &self.inner,
        )))
    }
}

impl<PK, K, V> BuildQueryRuntime<K, V> for NestedMap<PK, K, V>
where
    PK: CellValue + Hash + Eq,
    K: CellValue + Hash + Eq,
    V: CellValue,
{
    fn build_into(
        self,
        cx: &mut super::compiler::CompileContext,
        sink: crate::map_query::BoxedMapDiffSink<K, V>,
    ) -> Vec<SubscriptionGuard> {
        let identity = self.query_source_identity();
        install_reactive_source(&self, identity, cx, sink)
    }

    fn raw_source_identity(&self) -> Option<super::compiler::SourceIdentity> {
        Some(self.query_source_identity())
    }
}

impl<M> PlanProperties for M
where
    M: ReactiveMap + Clone,
    M::Key: CellValue + Hash + Eq,
    M::Value: CellValue,
{
    type Cardinality = ExactlyOne;
    type InputPartition = ByMapKey<M::Key>;
    type OutputPartition = ByMapKey<M::Key>;
}

#[allow(private_bounds)]
impl<K, V, M> MapQuery for CellMap<K, V, M>
where
    K: CellValue + Hash + Eq,
    V: CellValue,
    M: Clone + Send + Sync + 'static,
{
    type Key = K;
    type Value = V;

    /// No-op materialize: the cell map is already a cached, multicast source.
    /// Just flip the marker to `CellImmutable` and reuse the same `Arc<inner>`.
    fn materialize(self) -> CellMap<K, V, CellImmutable> {
        CellMap {
            inner: self.inner,
            _marker: PhantomData,
        }
    }
}

#[allow(private_bounds)]
impl<PK, K, V> MapQuery for NestedMap<PK, K, V>
where
    PK: CellValue + Hash + Eq,
    K: CellValue + Hash + Eq,
    V: CellValue,
{
    type Key = K;
    type Value = V;

    // Inherits the default materialize. A NestedMap is not a CellMap; it
    // owns its own diff-stream/state and there is no immutable variant to
    // short-circuit to, so the default allocate-and-forward strategy is
    // correct.
}