graphrecords_query/operations/string_operations/
split.rs1use crate::{
2 Bare, BareValueDomain, ExpandedChild, ExpandedIndex, Explain, Failure, IndexDomain, Indexed,
3 Labeled, Operand, Ordered, Positional, QueryResult,
4 capabilities::StringValue,
5 element::{Expanding, Pipeline, Retention},
6 error::string::EmptySplitDelimiter,
7 execution::EvaluationCache,
8 operations::{
9 Apply, ArgumentSource, ElementKernel, ElementPipeline, Keyed, Operation, OperationContext,
10 Prepare, Unaligned,
11 },
12 optimizer::{OperationInputs, OptimizerHints, PlanIdentity, PlanInputs},
13 registry::operation_manifest,
14 traits::Split,
15};
16use graphrecords_core::GraphRecord;
17
18#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
19#[operation(scope = Element)]
20#[explain(label = "Split")]
21#[plan(optimizer_hints(empty = if_all))]
22pub struct SplitOperation<A> {
23 #[argument]
24 delimiter: A,
25}
26
27impl<A: Prepare> Prepare for SplitOperation<A> {
28 type Prepared<'a>
29 = A::Prepared<'a>
30 where
31 Self: 'a;
32
33 fn prepare<'a>(
34 &'a self,
35 graphrecord: &'a GraphRecord,
36 cache: &'a EvaluationCache<'a>,
37 ) -> QueryResult<Self::Prepared<'a>> {
38 self.delimiter.prepare(graphrecord, cache)
39 }
40}
41
42impl<I, V, A> ElementKernel<Indexed<I, V>> for SplitOperation<A>
43where
44 I: IndexDomain,
45 V: StringValue,
46 A: ArgumentSource<Keyed<I>>,
47 A::ValueDomain: StringValue,
48{
49 type Emission = Expanding<Ordered>;
50 type OutShape = Indexed<ExpandedIndex<I, Positional>, V>;
51
52 fn pipeline<'a>(
53 _graphrecord: &'a GraphRecord,
54 prepared: Self::Prepared<'a>,
55 ) -> QueryResult<ElementPipeline<'a, Indexed<I, V>, Self>> {
56 Ok(Pipeline::keyed(move |index, value: V::Value<'a>| {
57 let role = value.clone();
58 let value =
59 V::into_string(Self::LABEL, value).map_err(|failure| failure.at::<I>(&index))?;
60 let delimiter = match A::Retention::collapse(A::resolve(&prepared, &index, Self::LABEL))
61 {
62 None => return Ok(Vec::new()),
63 Some(Err(failure)) => return Err(failure),
64 Some(Ok(delimiter)) => A::ValueDomain::into_string(Self::LABEL, delimiter)
65 .map_err(|failure| failure.at::<I>(&index))?,
66 };
67
68 if delimiter.is_empty() {
69 return Err(Failure::new_at::<I, _>(
70 Self::LABEL,
71 EmptySplitDelimiter,
72 &index,
73 ));
74 }
75
76 Ok(value
77 .split(&delimiter)
78 .enumerate()
79 .map(|(position, fragment)| {
80 ExpandedChild::success(position, V::from_string(&role, fragment.to_owned()))
81 })
82 .collect())
83 }))
84 }
85}
86
87impl<V, A> ElementKernel<Bare<V>> for SplitOperation<A>
88where
89 V: StringValue + BareValueDomain,
90 A: ArgumentSource<Unaligned>,
91 A::ValueDomain: StringValue,
92{
93 type Emission = Expanding<Ordered>;
94 type OutShape = Bare<V>;
95
96 fn pipeline<'a>(
97 _graphrecord: &'a GraphRecord,
98 prepared: Self::Prepared<'a>,
99 ) -> QueryResult<ElementPipeline<'a, Bare<V>, Self>> {
100 Ok(Pipeline::new(move |outcome: QueryResult<V::Value<'a>>| {
101 let (role, value) = match outcome {
102 Err(failure) => return vec![Err(failure)],
103 Ok(value) => {
104 let role = value.clone();
105 match V::into_string(Self::LABEL, value) {
106 Ok(value) => (role, value),
107 Err(failure) => return vec![Err(failure)],
108 }
109 }
110 };
111 let delimiter = match A::Retention::collapse(A::resolve(&prepared, &(), Self::LABEL)) {
112 None => return Vec::new(),
113 Some(Err(failure)) => return vec![Err(failure)],
114 Some(Ok(delimiter)) => match A::ValueDomain::into_string(Self::LABEL, delimiter) {
115 Ok(delimiter) => delimiter,
116 Err(failure) => return vec![Err(failure)],
117 },
118 };
119
120 if delimiter.is_empty() {
121 return vec![Err(Failure::new(Self::LABEL, EmptySplitDelimiter))];
122 }
123
124 value
125 .split(&delimiter)
126 .map(|fragment| Ok(V::from_string(&role, fragment.to_owned())))
127 .collect()
128 }))
129 }
130}
131
132impl<O, A> Split<A> for O
133where
134 SplitOperation<A>: Operation,
135 O: Apply<SplitOperation<A>>,
136{
137 type ReturnOperand = O::Output;
138
139 fn split(&self, delimiter: A) -> Self::ReturnOperand {
140 Self::ReturnOperand::new(OperationContext::new(
141 self.clone(),
142 SplitOperation { delimiter },
143 ))
144 }
145}
146
147operation_manifest! {
148 SplitOperation<A> {
149 method: Split<A>::split;
150 scope: element;
151
152 kernel {
153 parameters: <I: IndexDomain, V: StringValue>;
154 argument: A: ArgumentSource<Keyed<I>> where A::ValueDomain: StringValue;
155 input: Indexed<I, V>;
156 output: Indexed<ExpandedIndex<I, Positional>, V>;
157 emission: Expanding<Ordered>;
158 }
159 kernel {
160 parameters: <V: StringValue + BareValueDomain>;
161 argument: A: ArgumentSource<Unaligned> where A::ValueDomain: StringValue;
162 input: Bare<V>;
163 output: Bare<V>;
164 emission: Expanding<Ordered>;
165 }
166 }
167}