Skip to main content

graphrecords_query/operations/conversion/
transition.rs

1use crate::{
2    Bare, BareValueDomain, Explain, IndexDomain, Indexed, Labeled, Operand, QueryResult,
3    ValueDomain,
4    capabilities::ValueTransition,
5    element::{Pipeline, Preserving},
6    execution::EvaluationCache,
7    explain::ExplainFormatter,
8    operations::{Apply, ElementKernel, ElementPipeline, Operation, OperationContext, Prepare},
9    optimizer::{Estimate, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs, Stats},
10    traits::Transition,
11};
12use graphrecords_core::GraphRecord;
13use std::{
14    any::type_name,
15    fmt::{self, Write},
16    marker::PhantomData,
17};
18
19#[derive(Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
20#[operation(scope = Element)]
21#[plan(optimizer_hints(allows_limit_pushdown, empty = if_any))]
22pub struct TransitionOperation<T: ValueDomain> {
23    marker: PhantomData<fn() -> T>,
24}
25
26impl<T: ValueDomain> TransitionOperation<T> {
27    const fn new() -> Self {
28        Self {
29            marker: PhantomData,
30        }
31    }
32}
33
34impl<T: ValueDomain> Clone for TransitionOperation<T> {
35    fn clone(&self) -> Self {
36        Self::new()
37    }
38}
39
40impl<T: ValueDomain> Labeled for TransitionOperation<T> {
41    const LABEL: &'static str = "Transition";
42}
43
44impl<T: ValueDomain> Explain for TransitionOperation<T> {
45    fn describe<'a>(&'a self, formatter: &mut ExplainFormatter<'a, '_>) -> fmt::Result {
46        write!(formatter, "Transition target={}", type_name::<T>())
47    }
48}
49
50impl<T: ValueDomain> Prepare for TransitionOperation<T> {
51    type Prepared<'a> = ();
52
53    fn prepare<'a>(
54        &'a self,
55        _graphrecord: &'a GraphRecord,
56        _cache: &'a EvaluationCache<'a>,
57    ) -> QueryResult<Self::Prepared<'a>> {
58        Ok(())
59    }
60}
61
62impl<I, S, T> ElementKernel<Indexed<I, S>> for TransitionOperation<T>
63where
64    I: IndexDomain,
65    S: ValueTransition<T>,
66    T: ValueDomain,
67{
68    type Emission = Preserving;
69    type OutShape = Indexed<I, T>;
70
71    fn pipeline<'a>(
72        _graphrecord: &'a GraphRecord,
73        _prepared: Self::Prepared<'a>,
74    ) -> QueryResult<ElementPipeline<'a, Indexed<I, S>, Self>> {
75        Ok(Pipeline::keyed(|index, outcome: QueryResult<_>| {
76            outcome.and_then(|value| {
77                S::transition(Self::LABEL, value).map_err(|failure| failure.at::<I>(&index))
78            })
79        }))
80    }
81
82    fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
83        input.with_unknown_distinct()
84    }
85}
86
87impl<S, T> ElementKernel<Bare<S>> for TransitionOperation<T>
88where
89    S: ValueTransition<T> + BareValueDomain,
90    T: BareValueDomain,
91{
92    type Emission = Preserving;
93    type OutShape = Bare<T>;
94
95    fn pipeline<'a>(
96        _graphrecord: &'a GraphRecord,
97        _prepared: Self::Prepared<'a>,
98    ) -> QueryResult<ElementPipeline<'a, Bare<S>, Self>> {
99        Ok(Pipeline::new(|outcome: QueryResult<_>| {
100            outcome.and_then(|value| S::transition(Self::LABEL, value))
101        }))
102    }
103
104    fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
105        input.with_unknown_distinct()
106    }
107}
108
109impl<O: Operand> Transition for O {
110    type ReturnOperand<T>
111        = O::Output
112    where
113        T: ValueDomain,
114        O: Apply<TransitionOperation<T>>;
115
116    fn transition<T>(&self) -> Self::ReturnOperand<T>
117    where
118        T: ValueDomain,
119        Self: Apply<TransitionOperation<T>>,
120    {
121        Self::ReturnOperand::new(OperationContext::new(
122            self.clone(),
123            TransitionOperation::new(),
124        ))
125    }
126}