graphrecords-query 0.5.0

High-performance graph-based data records
Documentation
use crate::{
    BareValueDomain, ExpandedChild, ExpandedIndex, ExpandedIndexReference, Failure, IndexDomain,
    QueryResult, ValueDomain,
    element::{
        Arity, Bare, ElementEmission, ElementShape, Expanding, Indexed, OrderState, Ordered,
        Retention, Unordered,
    },
    error::index::DuplicateExpandedChildIndex,
};
use graphrecords_utils::aliases::GrHashSet;
use std::marker::PhantomData;

type ExpandedElement<'a, P, C, V> = (
    ExpandedIndexReference<'a, P, C>,
    QueryResult<<V as ValueDomain>::Value<'a>>,
);

pub struct Pipeline<'a, X: 'a, Y: 'a, E: ElementEmission> {
    run: Box<dyn Fn(X) -> Y + 'a>,
    emission: PhantomData<fn() -> E>,
}

impl<'a, X: 'a, Y: 'a, E: ElementEmission> Pipeline<'a, X, Y, E> {
    #[must_use]
    pub fn new(run: impl Fn(X) -> Y + 'a) -> Self {
        Self {
            run: Box::new(run),
            emission: PhantomData,
        }
    }

    pub(crate) fn run(&self, input: X) -> Y {
        (self.run)(input)
    }
}

impl<'a, A: 'a, B: 'a, Y: 'a, E: ElementEmission> Pipeline<'a, (A, B), Y, E> {
    #[must_use]
    pub fn keyed(run: impl Fn(A, B) -> Y + 'a) -> Self {
        Self::new(move |(first, second)| run(first, second))
    }

    #[must_use]
    pub fn unkeyed(run: impl Fn(B) -> Y + 'a) -> Self {
        Self::new(move |(_, second)| run(second))
    }
}

pub type IndexedValuePipeline<'a, I, V, W, E> = Pipeline<
    'a,
    (
        <I as IndexDomain>::Index<'a>,
        QueryResult<<V as ValueDomain>::Value<'a>>,
    ),
    <E as ElementEmission>::Step<QueryResult<<W as ValueDomain>::Value<'a>>>,
    E,
>;

pub type BarePipeline<'a, V, W, E> = Pipeline<
    'a,
    QueryResult<<V as ValueDomain>::Value<'a>>,
    <E as ElementEmission>::Step<QueryResult<<W as ValueDomain>::Value<'a>>>,
    E,
>;

pub type IndexedExpansionPipeline<'a, P, C, V, W, O> = Pipeline<
    'a,
    (<P as IndexDomain>::Index<'a>, <V as ValueDomain>::Value<'a>),
    QueryResult<Vec<ExpandedChild<'a, C, W>>>,
    Expanding<O>,
>;

pub trait ElementTransition<T: ElementShape, E: ElementEmission>: ElementShape {
    type Pipeline<'a>: 'a
    where
        Self: 'a,
        T: 'a;

    fn apply<'a, C: Arity>(
        values: C::Container<'a, Self::Element<'a>>,
        pipeline: Self::Pipeline<'a>,
    ) -> <E::OutArity<C> as Arity>::Container<'a, T::Element<'a>>;
}

impl<I: IndexDomain, V: ValueDomain, W: ValueDomain, E: Retention>
    ElementTransition<Indexed<I, W>, E> for Indexed<I, V>
{
    type Pipeline<'a> = IndexedValuePipeline<'a, I, V, W, E>;

    fn apply<'a, C: Arity>(
        values: C::Container<'a, Self::Element<'a>>,
        pipeline: Self::Pipeline<'a>,
    ) -> <E::OutArity<C> as Arity>::Container<'a, <Indexed<I, W> as ElementShape>::Element<'a>>
    {
        E::apply(values, move |element| {
            let (index, value): (I::Index<'a>, _) = element;
            let step = pipeline.run((index.clone(), value));

            <E as Retention>::map_step(step, |value| (index, value))
        })
    }
}

impl<I: IndexDomain, V: ValueDomain, W: BareValueDomain, E: ElementEmission>
    ElementTransition<Bare<W>, E> for Indexed<I, V>
{
    type Pipeline<'a> = IndexedValuePipeline<'a, I, V, W, E>;

    fn apply<'a, C: Arity>(
        values: C::Container<'a, Self::Element<'a>>,
        pipeline: Self::Pipeline<'a>,
    ) -> <E::OutArity<C> as Arity>::Container<'a, <Bare<W> as ElementShape>::Element<'a>> {
        E::apply(values, move |value| pipeline.run(value))
    }
}

impl<V: BareValueDomain, W: BareValueDomain, E: ElementEmission> ElementTransition<Bare<W>, E>
    for Bare<V>
{
    type Pipeline<'a> = BarePipeline<'a, V, W, E>;

    fn apply<'a, C: Arity>(
        values: C::Container<'a, Self::Element<'a>>,
        pipeline: Self::Pipeline<'a>,
    ) -> <E::OutArity<C> as Arity>::Container<'a, <Bare<W> as ElementShape>::Element<'a>> {
        E::apply(values, move |value| pipeline.run(value))
    }
}

fn expand_indexed_source<'a, P, C, V, W, O>(
    parent: P::Index<'a>,
    source: QueryResult<V::Value<'a>>,
    pipeline: &IndexedExpansionPipeline<'a, P, C, V, W, O>,
) -> Vec<ExpandedElement<'a, P, C, W>>
where
    P: IndexDomain,
    C: IndexDomain,
    V: ValueDomain,
    W: ValueDomain,
    O: OrderState,
    Expanding<O>: ElementEmission,
{
    let source_value = match source {
        Ok(value) => value,
        Err(failure) => {
            return vec![(ExpandedIndexReference::source(parent), Err(failure))];
        }
    };

    let children = match pipeline.run((parent.clone(), source_value)) {
        Ok(children) => children,
        Err(failure) => {
            return vec![(ExpandedIndexReference::source(parent), Err(failure))];
        }
    };

    let mut seen_children = GrHashSet::default();
    let mut fragment = Vec::with_capacity(children.len());

    for child in children {
        let (child_index, outcome) = child.into_parts();

        if !seen_children.insert(C::to_owned(&child_index)) {
            let source_address = ExpandedIndexReference::source(parent.clone());
            let failure = Failure::new_at::<ExpandedIndex<_, _>, _>(
                "indexed expansion",
                DuplicateExpandedChildIndex::<C>::new(C::to_owned(&child_index)),
                &source_address,
            );

            return vec![(source_address, Err(failure))];
        }

        fragment.push((
            ExpandedIndexReference::child(parent.clone(), child_index),
            outcome,
        ));
    }

    fragment
}

impl<P: IndexDomain, C: IndexDomain, V: ValueDomain, W: ValueDomain>
    ElementTransition<Indexed<ExpandedIndex<P, C>, W>, Expanding<Ordered>> for Indexed<P, V>
{
    type Pipeline<'a>
        = IndexedExpansionPipeline<'a, P, C, V, W, Ordered>
    where
        Self: 'a,
        Indexed<ExpandedIndex<P, C>, W>: 'a;

    fn apply<'a, A: Arity>(
        values: A::Container<'a, Self::Element<'a>>,
        pipeline: Self::Pipeline<'a>,
    ) -> <<Expanding<Ordered> as ElementEmission>::OutArity<A> as Arity>::Container<
        'a,
        <Indexed<ExpandedIndex<P, C>, W> as ElementShape>::Element<'a>,
    > {
        Expanding::<Ordered>::apply::<A, _, _>(values, move |(parent, source)| {
            expand_indexed_source::<_, _, V, _, _>(parent, source, &pipeline)
        })
    }
}

impl<P: IndexDomain, C: IndexDomain, V: ValueDomain, W: ValueDomain>
    ElementTransition<Indexed<ExpandedIndex<P, C>, W>, Expanding<Unordered>> for Indexed<P, V>
{
    type Pipeline<'a>
        = IndexedExpansionPipeline<'a, P, C, V, W, Unordered>
    where
        Self: 'a,
        Indexed<ExpandedIndex<P, C>, W>: 'a;

    fn apply<'a, A: Arity>(
        values: A::Container<'a, Self::Element<'a>>,
        pipeline: Self::Pipeline<'a>,
    ) -> <<Expanding<Unordered> as ElementEmission>::OutArity<A> as Arity>::Container<
        'a,
        <Indexed<ExpandedIndex<P, C>, W> as ElementShape>::Element<'a>,
    > {
        Expanding::<Unordered>::apply::<A, _, _>(values, move |(parent, source)| {
            expand_indexed_source::<_, _, V, _, _>(parent, source, &pipeline)
        })
    }
}