graphrecords_query/operations/conversion/
transition.rs1use 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}