graphrecords_query/operations/string_operations/
strip_suffix.rs1use super::{string_rebuild_argument_map_bare, string_rebuild_argument_map_indexed};
2use crate::{
3 Bare, BareValueDomain, Explain, IndexDomain, Indexed, Labeled, Operand, QueryResult,
4 capabilities::StringValue,
5 execution::EvaluationCache,
6 operations::{
7 Apply, ArgumentSource, ElementKernel, ElementPipeline, Keyed, Operation, OperationContext,
8 Prepare, Unaligned,
9 },
10 optimizer::{Estimate, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs, Stats},
11 registry::{describe::ArgumentRetention, operation_manifest},
12 traits::StripSuffix,
13};
14use graphrecords_core::GraphRecord;
15
16#[derive(Clone, Explain, Operation, OperationInputs, OptimizerHints, PlanIdentity, PlanInputs)]
17#[operation(scope = Element)]
18#[explain(label = "StripSuffix")]
19#[plan(optimizer_hints(empty = if_all))]
20pub struct StripSuffixOperation<A> {
21 #[argument]
22 suffix: A,
23}
24
25impl<A: Prepare> Prepare for StripSuffixOperation<A> {
26 type Prepared<'a>
27 = A::Prepared<'a>
28 where
29 Self: 'a;
30
31 fn prepare<'a>(
32 &'a self,
33 graphrecord: &'a GraphRecord,
34 cache: &'a EvaluationCache<'a>,
35 ) -> QueryResult<Self::Prepared<'a>> {
36 self.suffix.prepare(graphrecord, cache)
37 }
38}
39
40impl<I, V, A> ElementKernel<Indexed<I, V>> for StripSuffixOperation<A>
41where
42 I: IndexDomain,
43 V: StringValue,
44 A: ArgumentSource<Keyed<I>>,
45 A::ValueDomain: StringValue,
46{
47 type Emission = A::Retention;
48 type OutShape = Indexed<I, V>;
49
50 fn pipeline<'a>(
51 _graphrecord: &'a GraphRecord,
52 prepared: Self::Prepared<'a>,
53 ) -> QueryResult<ElementPipeline<'a, Indexed<I, V>, Self>> {
54 Ok(string_rebuild_argument_map_indexed::<I, V, A>(
55 prepared,
56 Self::LABEL,
57 |_, value, suffix| {
58 let value = match value.strip_suffix(&suffix) {
59 Some(stripped) => stripped.to_string(),
60 None => value,
61 };
62
63 Ok(value)
64 },
65 ))
66 }
67
68 fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
69 input.with_unknown_distinct()
70 }
71}
72
73impl<V, A> ElementKernel<Bare<V>> for StripSuffixOperation<A>
74where
75 V: StringValue + BareValueDomain,
76 A: ArgumentSource<Unaligned>,
77 A::ValueDomain: StringValue,
78{
79 type Emission = A::Retention;
80 type OutShape = Bare<V>;
81
82 fn pipeline<'a>(
83 _graphrecord: &'a GraphRecord,
84 prepared: Self::Prepared<'a>,
85 ) -> QueryResult<ElementPipeline<'a, Bare<V>, Self>> {
86 Ok(string_rebuild_argument_map_bare::<V, A>(
87 prepared,
88 Self::LABEL,
89 |_, value, suffix| {
90 let value = match value.strip_suffix(&suffix) {
91 Some(stripped) => stripped.to_string(),
92 None => value,
93 };
94
95 Ok(value)
96 },
97 ))
98 }
99
100 fn estimate(&self, input: Estimate, _stats: &Stats) -> Estimate {
101 input.with_unknown_distinct()
102 }
103}
104
105impl<O, A> StripSuffix<A> for O
106where
107 StripSuffixOperation<A>: Operation,
108 O: Apply<StripSuffixOperation<A>>,
109{
110 type ReturnOperand = O::Output;
111
112 fn strip_suffix(&self, suffix: A) -> Self::ReturnOperand {
113 Self::ReturnOperand::new(OperationContext::new(
114 self.clone(),
115 StripSuffixOperation { suffix },
116 ))
117 }
118}
119
120operation_manifest! {
121 StripSuffixOperation<A> {
122 method: StripSuffix<A>::strip_suffix;
123 scope: element;
124
125 kernel {
126 parameters: <I: IndexDomain, V: StringValue>;
127 argument: A: ArgumentSource<Keyed<I>> where A::ValueDomain: StringValue;
128 input: Indexed<I, V>;
129 output: Indexed<I, V>;
130 emission: ArgumentRetention;
131 }
132 kernel {
133 parameters: <V: StringValue + BareValueDomain>;
134 argument: A: ArgumentSource<Unaligned> where A::ValueDomain: StringValue;
135 input: Bare<V>;
136 output: Bare<V>;
137 emission: ArgumentRetention;
138 }
139 }
140}