graphrecords_query/operations/string_operations/
slice.rs1use super::{string_rebuild_map_bare, string_rebuild_map_indexed};
2use crate::{
3 Bare, BareValueDomain, Explain, Failure, IndexDomain, Indexed, Labeled, Operand, QueryResult,
4 capabilities::StringValue,
5 element::Preserving,
6 error::string::InvalidStringSlice,
7 execution::EvaluationCache,
8 operations::{Apply, ElementKernel, ElementPipeline, Operation, OperationContext, Prepare},
9 optimizer::{Estimate, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs, Stats},
10 registry::operation_manifest,
11 traits::Slice,
12};
13use graphrecords_core::GraphRecord;
14
15fn slice_string(label: &'static str, value: &str, start: usize, end: usize) -> QueryResult<String> {
16 let characters: Vec<_> = value.chars().collect();
17
18 if start > end || end > characters.len() {
19 return Err(Failure::new(
20 label,
21 InvalidStringSlice::new(start, end, characters.len()),
22 ));
23 }
24
25 Ok(characters[start..end].iter().collect())
26}
27
28#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
29#[operation(scope = Element)]
30#[explain(label = "Slice")]
31#[plan(optimizer_hints(allows_limit_pushdown, empty = if_any))]
32pub struct SliceOperation {
33 #[explain(label)]
34 start: usize,
35 #[explain(label)]
36 end: usize,
37}
38
39impl Prepare for SliceOperation {
40 type Prepared<'a> = (usize, usize);
41
42 fn prepare<'a>(
43 &'a self,
44 _graphrecord: &'a GraphRecord,
45 _cache: &'a EvaluationCache<'a>,
46 ) -> QueryResult<Self::Prepared<'a>> {
47 Ok((self.start, self.end))
48 }
49}
50
51impl<I, V> ElementKernel<Indexed<I, V>> for SliceOperation
52where
53 I: IndexDomain,
54 V: StringValue,
55{
56 type Emission = Preserving;
57 type OutShape = Indexed<I, V>;
58
59 fn pipeline<'a>(
60 _graphrecord: &'a GraphRecord,
61 prepared: Self::Prepared<'a>,
62 ) -> QueryResult<ElementPipeline<'a, Indexed<I, V>, Self>> {
63 Ok(string_rebuild_map_indexed::<I, V>(
64 Self::LABEL,
65 move |label, value| slice_string(label, &value, prepared.0, prepared.1),
66 ))
67 }
68
69 fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
70 input.with_unknown_distinct()
71 }
72}
73
74impl<V> ElementKernel<Bare<V>> for SliceOperation
75where
76 V: StringValue + BareValueDomain,
77{
78 type Emission = Preserving;
79 type OutShape = Bare<V>;
80
81 fn pipeline<'a>(
82 _graphrecord: &'a GraphRecord,
83 prepared: Self::Prepared<'a>,
84 ) -> QueryResult<ElementPipeline<'a, Bare<V>, Self>> {
85 Ok(string_rebuild_map_bare::<V>(
86 Self::LABEL,
87 move |label, value| slice_string(label, &value, prepared.0, prepared.1),
88 ))
89 }
90
91 fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
92 input.with_unknown_distinct()
93 }
94}
95
96impl<O: Apply<SliceOperation>> Slice for O {
97 type ReturnOperand = O::Output;
98
99 fn slice(&self, start: usize, end: usize) -> Self::ReturnOperand {
100 Self::ReturnOperand::new(OperationContext::new(
101 self.clone(),
102 SliceOperation { start, end },
103 ))
104 }
105}
106
107operation_manifest! {
108 SliceOperation {
109 method: Slice::slice;
110 scope: element;
111
112 kernel {
113 parameters: <I: IndexDomain, V: StringValue>;
114 field: start: usize;
115 field: end: usize;
116 input: Indexed<I, V>;
117 output: Indexed<I, V>;
118 emission: Preserving;
119 }
120 kernel {
121 parameters: <V: StringValue + BareValueDomain>;
122 field: start: usize;
123 field: end: usize;
124 input: Bare<V>;
125 output: Bare<V>;
126 emission: Preserving;
127 }
128 }
129}