graphrecords-query 0.5.0

High-performance graph-based data records
Documentation
use crate::{
    Bare, BareValueDomain, Explain, IndexDomain, Indexed, Multiple, OrderState, QueryResult,
    Single, ValueDomain,
    element::{Dropping, ElementEmission, Retention},
    execution::EvaluationCache,
    explain::ExplainFormatter,
    operands::OperandHandle,
    operations::{
        Alignment, ArgumentSource, Keyed, Lookup, Prepare, SourceDomain,
        policy::{Drop, Replace},
    },
    optimizer::{Estimate, Estimated, PlanIdentity, PlanInputs, PlanNode, Stats},
};
use graphrecords_core::GraphRecord;
use std::{
    fmt::{self, Write},
    hash::Hasher,
    marker::PhantomData,
};

pub trait MaybeAbsent<A: Alignment>: ArgumentSource<A> {
    fn on_missing<P>(self, policy: P) -> WithMissing<A, Self, P>
    where
        Self: Sized,
        P: MissingPolicy<A, Self>,
    {
        WithMissing::new(self, policy)
    }
}

pub trait MissingPolicy<A: Alignment, S: SourceDomain>:
    Send + Sync + Clone + 'static + Explain + PlanIdentity + PlanInputs
{
    type Retention: Retention;

    type Prepared<'a>: Clone + 'a
    where
        Self: 'a;

    fn prepare<'a>(
        &'a self,
        graphrecord: &'a GraphRecord,
        cache: &'a EvaluationCache<'a>,
    ) -> QueryResult<Self::Prepared<'a>>;

    fn resolve_absent<'a>(
        prepared: &Self::Prepared<'a>,
        address: &A::Address<'a>,
        label: &'static str,
    ) -> <Self::Retention as ElementEmission>::Step<
        QueryResult<<S::ValueDomain as ValueDomain>::Value<'a>>,
    >
    where
        S: 'a;
}

impl<A: Alignment, S: SourceDomain> MissingPolicy<A, S> for Drop {
    type Prepared<'a> = ();
    type Retention = Dropping;

    fn prepare<'a>(
        &'a self,
        _graphrecord: &'a GraphRecord,
        _cache: &'a EvaluationCache<'a>,
    ) -> QueryResult<Self::Prepared<'a>> {
        Ok(())
    }

    fn resolve_absent<'a>(
        _prepared: &Self::Prepared<'a>,
        _address: &A::Address<'a>,
        _label: &'static str,
    ) -> <Self::Retention as ElementEmission>::Step<
        QueryResult<<S::ValueDomain as ValueDomain>::Value<'a>>,
    >
    where
        S: 'a,
    {
        None
    }
}

impl<A, S, R> MissingPolicy<A, S> for Replace<R>
where
    A: Alignment,
    S: SourceDomain,
    R: ArgumentSource<A, S::ValueDomain> + Clone,
{
    type Prepared<'a> = R::Prepared<'a>;
    type Retention = R::Retention;

    fn prepare<'a>(
        &'a self,
        graphrecord: &'a GraphRecord,
        cache: &'a EvaluationCache<'a>,
    ) -> QueryResult<Self::Prepared<'a>> {
        self.replacement().prepare(graphrecord, cache)
    }

    fn resolve_absent<'a>(
        prepared: &Self::Prepared<'a>,
        address: &A::Address<'a>,
        label: &'static str,
    ) -> <Self::Retention as ElementEmission>::Step<
        QueryResult<<S::ValueDomain as ValueDomain>::Value<'a>>,
    >
    where
        S: 'a,
    {
        R::resolve(prepared, address, label)
    }
}

impl<I: IndexDomain, V: ValueDomain, O: OrderState> MaybeAbsent<Keyed<I>>
    for OperandHandle<Indexed<I, V>, Multiple<O>>
{
}
impl<A: Alignment, V: BareValueDomain> MaybeAbsent<A> for OperandHandle<Bare<V>, Single> {}

pub struct WithMissing<A: Alignment, S: MaybeAbsent<A>, P> {
    inner: S,
    policy: P,
    alignment: PhantomData<fn() -> A>,
}

impl<A: Alignment, S: MaybeAbsent<A>, P> WithMissing<A, S, P> {
    #[must_use]
    pub fn new(inner: S, policy: P) -> Self {
        Self {
            inner,
            policy,
            alignment: PhantomData,
        }
    }
}

impl<A: Alignment, S: MaybeAbsent<A> + Clone, P: Clone> Clone for WithMissing<A, S, P> {
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
            policy: self.policy.clone(),
            alignment: PhantomData,
        }
    }
}

impl<A: Alignment, S: MaybeAbsent<A>, P: Explain> Explain for WithMissing<A, S, P> {
    fn describe<'a>(&'a self, formatter: &mut ExplainFormatter<'a, '_>) -> fmt::Result {
        self.inner.describe(formatter)?;
        formatter.write_str(" on_missing(")?;
        self.policy.describe(formatter)?;
        formatter.write_str(")")
    }
}

impl<A: Alignment, S: MaybeAbsent<A>, P: PlanIdentity> PlanIdentity for WithMissing<A, S, P> {
    fn identity_eq(&self, other: &Self) -> bool {
        self.inner.identity_eq(&other.inner) && self.policy.identity_eq(&other.policy)
    }

    fn identity_hash<H: Hasher>(&self, state: &mut H) {
        self.inner.identity_hash(state);
        self.policy.identity_hash(state);
    }
}

impl<A: Alignment, S: MaybeAbsent<A>, P: PlanInputs> PlanInputs for WithMissing<A, S, P> {
    fn inputs(&self) -> Vec<&dyn PlanNode> {
        let mut inputs = self.inner.inputs();
        inputs.extend(self.policy.inputs());

        inputs
    }
}

impl<A: Alignment, S: MaybeAbsent<A>, P: MissingPolicy<A, S>> Prepare for WithMissing<A, S, P> {
    type Prepared<'a>
        = (S::Prepared<'a>, P::Prepared<'a>)
    where
        Self: 'a;

    fn prepare<'a>(
        &'a self,
        graphrecord: &'a GraphRecord,
        cache: &'a EvaluationCache<'a>,
    ) -> QueryResult<Self::Prepared<'a>> {
        Ok((
            self.inner.prepare(graphrecord, cache)?,
            self.policy.prepare(graphrecord, cache)?,
        ))
    }
}

impl<A: Alignment, S: MaybeAbsent<A>, P> Estimated for WithMissing<A, S, P> {
    fn estimate(&self, stats: &Stats) -> Estimate {
        self.inner.estimate(stats)
    }
}

impl<A: Alignment, S: MaybeAbsent<A>, P> SourceDomain for WithMissing<A, S, P> {
    type ValueDomain = S::ValueDomain;
}

impl<A: Alignment, S: MaybeAbsent<A>, P: MissingPolicy<A, S>> ArgumentSource<A>
    for WithMissing<A, S, P>
{
    type Retention = P::Retention;

    fn lookup<'a, 'prepared>(
        prepared: &'prepared Self::Prepared<'a>,
        address: &A::Address<'a>,
    ) -> Lookup<'prepared, QueryResult<<S::ValueDomain as ValueDomain>::Value<'a>>>
    where
        Self: 'a,
    {
        S::lookup(&prepared.0, address)
    }

    fn resolve<'a>(
        prepared: &Self::Prepared<'a>,
        address: &A::Address<'a>,
        label: &'static str,
    ) -> <Self::Retention as ElementEmission>::Step<
        QueryResult<<S::ValueDomain as ValueDomain>::Value<'a>>,
    >
    where
        Self: 'a,
    {
        match S::lookup(&prepared.0, address) {
            Lookup::Present(wrapped) => P::Retention::keep(wrapped.clone()),
            Lookup::Absent(_) => P::resolve_absent(&prepared.1, address, label),
        }
    }
}