1use crate::{
2 BareValueDomain, ExpandedChild, ExpandedIndex, ExpandedIndexReference, Failure, IndexDomain,
3 QueryResult, ValueDomain,
4 element::{
5 Arity, Bare, ElementEmission, ElementShape, Expanding, Indexed, OrderState, Ordered,
6 Retention, Unordered,
7 },
8 error::index::DuplicateExpandedChildIndex,
9};
10use graphrecords_utils::aliases::GrHashSet;
11use std::marker::PhantomData;
12
13type ExpandedElement<'a, P, C, V> = (
14 ExpandedIndexReference<'a, P, C>,
15 QueryResult<<V as ValueDomain>::Value<'a>>,
16);
17
18pub struct Pipeline<'a, X: 'a, Y: 'a, E: ElementEmission> {
19 run: Box<dyn Fn(X) -> Y + 'a>,
20 emission: PhantomData<fn() -> E>,
21}
22
23impl<'a, X: 'a, Y: 'a, E: ElementEmission> Pipeline<'a, X, Y, E> {
24 #[must_use]
25 pub fn new(run: impl Fn(X) -> Y + 'a) -> Self {
26 Self {
27 run: Box::new(run),
28 emission: PhantomData,
29 }
30 }
31
32 pub(crate) fn run(&self, input: X) -> Y {
33 (self.run)(input)
34 }
35}
36
37impl<'a, A: 'a, B: 'a, Y: 'a, E: ElementEmission> Pipeline<'a, (A, B), Y, E> {
38 #[must_use]
39 pub fn keyed(run: impl Fn(A, B) -> Y + 'a) -> Self {
40 Self::new(move |(first, second)| run(first, second))
41 }
42
43 #[must_use]
44 pub fn unkeyed(run: impl Fn(B) -> Y + 'a) -> Self {
45 Self::new(move |(_, second)| run(second))
46 }
47}
48
49pub type IndexedValuePipeline<'a, I, V, W, E> = Pipeline<
50 'a,
51 (
52 <I as IndexDomain>::Index<'a>,
53 QueryResult<<V as ValueDomain>::Value<'a>>,
54 ),
55 <E as ElementEmission>::Step<QueryResult<<W as ValueDomain>::Value<'a>>>,
56 E,
57>;
58
59pub type BarePipeline<'a, V, W, E> = Pipeline<
60 'a,
61 QueryResult<<V as ValueDomain>::Value<'a>>,
62 <E as ElementEmission>::Step<QueryResult<<W as ValueDomain>::Value<'a>>>,
63 E,
64>;
65
66pub type IndexedExpansionPipeline<'a, P, C, V, W, O> = Pipeline<
67 'a,
68 (<P as IndexDomain>::Index<'a>, <V as ValueDomain>::Value<'a>),
69 QueryResult<Vec<ExpandedChild<'a, C, W>>>,
70 Expanding<O>,
71>;
72
73pub trait ElementTransition<T: ElementShape, E: ElementEmission>: ElementShape {
74 type Pipeline<'a>: 'a
75 where
76 Self: 'a,
77 T: 'a;
78
79 fn apply<'a, C: Arity>(
80 values: C::Container<'a, Self::Element<'a>>,
81 pipeline: Self::Pipeline<'a>,
82 ) -> <E::OutArity<C> as Arity>::Container<'a, T::Element<'a>>;
83}
84
85impl<I: IndexDomain, V: ValueDomain, W: ValueDomain, E: Retention>
86 ElementTransition<Indexed<I, W>, E> for Indexed<I, V>
87{
88 type Pipeline<'a> = IndexedValuePipeline<'a, I, V, W, E>;
89
90 fn apply<'a, C: Arity>(
91 values: C::Container<'a, Self::Element<'a>>,
92 pipeline: Self::Pipeline<'a>,
93 ) -> <E::OutArity<C> as Arity>::Container<'a, <Indexed<I, W> as ElementShape>::Element<'a>>
94 {
95 E::apply(values, move |element| {
96 let (index, value): (I::Index<'a>, _) = element;
97 let step = pipeline.run((index.clone(), value));
98
99 <E as Retention>::map_step(step, |value| (index, value))
100 })
101 }
102}
103
104impl<I: IndexDomain, V: ValueDomain, W: BareValueDomain, E: ElementEmission>
105 ElementTransition<Bare<W>, E> for Indexed<I, V>
106{
107 type Pipeline<'a> = IndexedValuePipeline<'a, I, V, W, E>;
108
109 fn apply<'a, C: Arity>(
110 values: C::Container<'a, Self::Element<'a>>,
111 pipeline: Self::Pipeline<'a>,
112 ) -> <E::OutArity<C> as Arity>::Container<'a, <Bare<W> as ElementShape>::Element<'a>> {
113 E::apply(values, move |value| pipeline.run(value))
114 }
115}
116
117impl<V: BareValueDomain, W: BareValueDomain, E: ElementEmission> ElementTransition<Bare<W>, E>
118 for Bare<V>
119{
120 type Pipeline<'a> = BarePipeline<'a, V, W, E>;
121
122 fn apply<'a, C: Arity>(
123 values: C::Container<'a, Self::Element<'a>>,
124 pipeline: Self::Pipeline<'a>,
125 ) -> <E::OutArity<C> as Arity>::Container<'a, <Bare<W> as ElementShape>::Element<'a>> {
126 E::apply(values, move |value| pipeline.run(value))
127 }
128}
129
130fn expand_indexed_source<'a, P, C, V, W, O>(
131 parent: P::Index<'a>,
132 source: QueryResult<V::Value<'a>>,
133 pipeline: &IndexedExpansionPipeline<'a, P, C, V, W, O>,
134) -> Vec<ExpandedElement<'a, P, C, W>>
135where
136 P: IndexDomain,
137 C: IndexDomain,
138 V: ValueDomain,
139 W: ValueDomain,
140 O: OrderState,
141 Expanding<O>: ElementEmission,
142{
143 let source_value = match source {
144 Ok(value) => value,
145 Err(failure) => {
146 return vec![(ExpandedIndexReference::source(parent), Err(failure))];
147 }
148 };
149
150 let children = match pipeline.run((parent.clone(), source_value)) {
151 Ok(children) => children,
152 Err(failure) => {
153 return vec![(ExpandedIndexReference::source(parent), Err(failure))];
154 }
155 };
156
157 let mut seen_children = GrHashSet::default();
158 let mut fragment = Vec::with_capacity(children.len());
159
160 for child in children {
161 let (child_index, outcome) = child.into_parts();
162
163 if !seen_children.insert(C::to_owned(&child_index)) {
164 let source_address = ExpandedIndexReference::source(parent.clone());
165 let failure = Failure::new_at::<ExpandedIndex<_, _>, _>(
166 "indexed expansion",
167 DuplicateExpandedChildIndex::<C>::new(C::to_owned(&child_index)),
168 &source_address,
169 );
170
171 return vec![(source_address, Err(failure))];
172 }
173
174 fragment.push((
175 ExpandedIndexReference::child(parent.clone(), child_index),
176 outcome,
177 ));
178 }
179
180 fragment
181}
182
183impl<P: IndexDomain, C: IndexDomain, V: ValueDomain, W: ValueDomain>
184 ElementTransition<Indexed<ExpandedIndex<P, C>, W>, Expanding<Ordered>> for Indexed<P, V>
185{
186 type Pipeline<'a>
187 = IndexedExpansionPipeline<'a, P, C, V, W, Ordered>
188 where
189 Self: 'a,
190 Indexed<ExpandedIndex<P, C>, W>: 'a;
191
192 fn apply<'a, A: Arity>(
193 values: A::Container<'a, Self::Element<'a>>,
194 pipeline: Self::Pipeline<'a>,
195 ) -> <<Expanding<Ordered> as ElementEmission>::OutArity<A> as Arity>::Container<
196 'a,
197 <Indexed<ExpandedIndex<P, C>, W> as ElementShape>::Element<'a>,
198 > {
199 Expanding::<Ordered>::apply::<A, _, _>(values, move |(parent, source)| {
200 expand_indexed_source::<_, _, V, _, _>(parent, source, &pipeline)
201 })
202 }
203}
204
205impl<P: IndexDomain, C: IndexDomain, V: ValueDomain, W: ValueDomain>
206 ElementTransition<Indexed<ExpandedIndex<P, C>, W>, Expanding<Unordered>> for Indexed<P, V>
207{
208 type Pipeline<'a>
209 = IndexedExpansionPipeline<'a, P, C, V, W, Unordered>
210 where
211 Self: 'a,
212 Indexed<ExpandedIndex<P, C>, W>: 'a;
213
214 fn apply<'a, A: Arity>(
215 values: A::Container<'a, Self::Element<'a>>,
216 pipeline: Self::Pipeline<'a>,
217 ) -> <<Expanding<Unordered> as ElementEmission>::OutArity<A> as Arity>::Container<
218 'a,
219 <Indexed<ExpandedIndex<P, C>, W> as ElementShape>::Element<'a>,
220 > {
221 Expanding::<Unordered>::apply::<A, _, _>(values, move |(parent, source)| {
222 expand_indexed_source::<_, _, V, _, _>(parent, source, &pipeline)
223 })
224 }
225}