• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gluesql / gluesql / 18519170559

15 Oct 2025 05:50AM UTC coverage: 97.964% (+0.02%) from 97.941%
18519170559

push

github

web-flow
Add JSON -> operator support  (#1807)

Adds Arrow operator (->): AST variant and SQL emission, translator mapping, evaluator dispatch, a new select function for Map/List, two new EvaluateError variants, duplicate Evaluated::arrow implementations, and new tests exercising arrow behavior.

52 of 59 new or added lines in 5 files covered. (88.14%)

38438 of 39237 relevant lines covered (97.96%)

72244.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.45
/core/src/executor/evaluate/evaluated.rs
1
use {
2
    super::{error::EvaluateError, function},
3
    crate::{
4
        ast::{BinaryOperator, DataType, TrimWhereField},
5
        data::{Key, Literal, Value, value::BTreeMapJsonExt},
6
        result::{Error, Result},
7
    },
8
    std::{borrow::Cow, cmp::Ordering, collections::BTreeMap, convert::TryFrom, ops::Range},
9
    utils::Tribool,
10
};
11

12
#[derive(Clone, Debug, PartialEq)]
13
pub enum Evaluated<'a> {
14
    Literal(Literal<'a>),
15
    StrSlice {
16
        source: Cow<'a, str>,
17
        range: Range<usize>,
18
    },
19
    Value(Value),
20
}
21

22
impl TryFrom<Evaluated<'_>> for Value {
23
    type Error = Error;
24

25
    fn try_from(e: Evaluated<'_>) -> Result<Value> {
6,782,669✔
26
        match e {
6,782,669✔
27
            Evaluated::Literal(v) => Value::try_from(v),
2,047,488✔
28
            Evaluated::StrSlice {
29
                source: s,
79,730✔
30
                range: r,
79,730✔
31
            } => Ok(Value::Str(s[r].to_owned())),
79,730✔
32
            Evaluated::Value(v) => Ok(v),
4,655,451✔
33
        }
34
    }
6,782,669✔
35
}
36

37
impl TryFrom<Evaluated<'_>> for Key {
38
    type Error = Error;
39

40
    fn try_from(evaluated: Evaluated<'_>) -> Result<Self> {
2,039,347✔
41
        Self::try_from(&evaluated)
2,039,347✔
42
    }
2,039,347✔
43
}
44

45
impl TryFrom<&Evaluated<'_>> for Key {
46
    type Error = Error;
47

48
    fn try_from(evaluated: &Evaluated<'_>) -> Result<Self> {
2,039,347✔
49
        match evaluated {
2,039,347✔
50
            Evaluated::Literal(l) => Value::try_from(l)?.try_into(),
67,836✔
51
            Evaluated::StrSlice { source, range } => Ok(Key::Str(source[range.clone()].to_owned())),
1✔
52
            Evaluated::Value(v) => v.try_into(),
1,971,510✔
53
        }
54
    }
2,039,347✔
55
}
56

57
impl TryFrom<Evaluated<'_>> for bool {
58
    type Error = Error;
59

60
    fn try_from(e: Evaluated<'_>) -> Result<bool> {
4,860,980✔
61
        match e {
31,280✔
62
            Evaluated::Literal(Literal::Boolean(v)) => Ok(v),
30,090✔
63
            Evaluated::Literal(v) => {
1,190✔
64
                Err(EvaluateError::BooleanTypeRequired(format!("{v:?}")).into())
1,190✔
65
            }
66
            Evaluated::StrSlice { source, range } => {
1,190✔
67
                Err(EvaluateError::BooleanTypeRequired(source[range].to_owned()).into())
1,190✔
68
            }
69
            Evaluated::Value(Value::Bool(v)) => Ok(v),
4,827,320✔
70
            Evaluated::Value(v) => Err(EvaluateError::BooleanTypeRequired(format!("{v:?}")).into()),
1,190✔
71
        }
72
    }
4,860,980✔
73
}
74

75
impl TryFrom<Evaluated<'_>> for BTreeMap<String, Value> {
76
    type Error = Error;
77

78
    fn try_from(evaluated: Evaluated<'_>) -> Result<BTreeMap<String, Value>> {
19,890✔
79
        match evaluated {
17,510✔
80
            Evaluated::Literal(Literal::Text(v)) => BTreeMap::parse_json_object(v.as_ref()),
16,320✔
81
            Evaluated::Literal(v) => {
1,190✔
82
                Err(EvaluateError::TextLiteralRequired(format!("{v:?}")).into())
1,190✔
83
            }
84
            Evaluated::Value(Value::Str(v)) => BTreeMap::parse_json_object(v.as_str()),
×
85
            Evaluated::Value(Value::Map(v)) => Ok(v),
×
86
            Evaluated::Value(v) => Err(EvaluateError::MapOrStringValueRequired(v.into()).into()),
1,190✔
87
            Evaluated::StrSlice { source, range } => BTreeMap::parse_json_object(&source[range]),
1,190✔
88
        }
89
    }
19,890✔
90
}
91

92
fn binary_op<'a, 'b, T, U>(
606,900✔
93
    l: &Evaluated<'a>,
606,900✔
94
    r: &Evaluated<'b>,
606,900✔
95
    op: BinaryOperator,
606,900✔
96
    value_op: T,
606,900✔
97
    literal_op: U,
606,900✔
98
) -> Result<Evaluated<'b>>
606,900✔
99
where
606,900✔
100
    T: FnOnce(&Value, &Value) -> Result<Value>,
606,900✔
101
    U: FnOnce(&Literal<'a>, &Literal<'b>) -> Result<Literal<'b>>,
606,900✔
102
{
103
    match (l, r) {
606,900✔
104
        (Evaluated::Literal(l), Evaluated::Literal(r)) => literal_op(l, r).map(Evaluated::Literal),
112,115✔
105
        (Evaluated::Literal(l), Evaluated::Value(r)) => {
45,220✔
106
            value_op(&Value::try_from(l)?, r).map(Evaluated::Value)
45,220✔
107
        }
108
        (Evaluated::Value(l), Evaluated::Literal(r)) => {
176,545✔
109
            value_op(l, &Value::try_from(r)?).map(Evaluated::Value)
176,545✔
110
        }
111
        (Evaluated::Value(l), Evaluated::Value(r)) => value_op(l, r).map(Evaluated::Value),
271,830✔
112
        (l, r) => Err(EvaluateError::UnsupportedBinaryOperation {
1,190✔
113
            left: format!("{l:?}"),
1,190✔
114
            op,
1,190✔
115
            right: format!("{r:?}"),
1,190✔
116
        }
1,190✔
117
        .into()),
1,190✔
118
    }
119
}
606,900✔
120

121
pub fn exceptional_int_val_to_eval<'a>(name: String, v: Value) -> Result<Evaluated<'a>> {
3,570✔
122
    match v {
3,570✔
123
        Value::Null => Ok(Evaluated::Value(Value::Null)),
2,380✔
124
        _ => Err(EvaluateError::FunctionRequiresIntegerValue(name).into()),
1,190✔
125
    }
126
}
3,570✔
127

128
impl From<Tribool> for Evaluated<'_> {
129
    fn from(x: Tribool) -> Self {
2,675,290✔
130
        Evaluated::Value(Value::from(x))
2,675,290✔
131
    }
2,675,290✔
132
}
133

134
impl<'a> Evaluated<'a> {
135
    pub fn evaluate_eq(&self, other: &Evaluated<'a>) -> Tribool {
3,932,780✔
136
        match (self, other) {
3,932,780✔
137
            (Evaluated::Literal(a), Evaluated::Literal(b)) => a.evaluate_eq(b),
42,840✔
138
            (Evaluated::Literal(b), Evaluated::Value(a))
1,010,735✔
139
            | (Evaluated::Value(a), Evaluated::Literal(b)) => a.evaluate_eq_with_literal(b),
2,155,685✔
140
            (Evaluated::Value(a), Evaluated::Value(b)) => a.evaluate_eq(b),
1,716,405✔
141
            (Evaluated::Literal(a), Evaluated::StrSlice { source, range })
3,570✔
142
            | (Evaluated::StrSlice { source, range }, Evaluated::Literal(a)) => {
3,570✔
143
                let b = &source[range.clone()];
7,140✔
144
                a.evaluate_eq(&Literal::Text(Cow::Borrowed(b)))
7,140✔
145
            }
146
            (Evaluated::Value(a), Evaluated::StrSlice { source, range })
3,570✔
147
            | (Evaluated::StrSlice { source, range }, Evaluated::Value(a)) => {
3,570✔
148
                let b = &source[range.clone()];
7,140✔
149
                a.evaluate_eq_with_literal(&Literal::Text(Cow::Borrowed(b)))
7,140✔
150
            }
151
            (
152
                Evaluated::StrSlice { source, range },
3,570✔
153
                Evaluated::StrSlice {
154
                    source: source2,
3,570✔
155
                    range: range2,
3,570✔
156
                },
157
            ) => Tribool::from(source[range.clone()] == source2[range2.clone()]),
3,570✔
158
        }
159
    }
3,932,780✔
160

161
    pub fn evaluate_cmp(&self, other: &Evaluated<'a>) -> Option<Ordering> {
743,750✔
162
        match (self, other) {
743,750✔
163
            (Evaluated::Literal(l), Evaluated::Literal(r)) => l.evaluate_cmp(r),
83,300✔
164
            (Evaluated::Literal(l), Evaluated::Value(r)) => {
69,020✔
165
                r.evaluate_cmp_with_literal(l).map(|o| o.reverse())
69,020✔
166
            }
167
            (Evaluated::Value(l), Evaluated::Literal(r)) => l.evaluate_cmp_with_literal(r),
452,540✔
168
            (Evaluated::Value(l), Evaluated::Value(r)) => l.evaluate_cmp(r),
121,040✔
169
            (Evaluated::Literal(l), Evaluated::StrSlice { source, range }) => {
3,570✔
170
                let r = Literal::Text(Cow::Borrowed(&source[range.clone()]));
3,570✔
171

172
                l.evaluate_cmp(&r)
3,570✔
173
            }
174
            (Evaluated::Value(l), Evaluated::StrSlice { source, range }) => {
3,570✔
175
                let r = Literal::Text(Cow::Borrowed(&source[range.clone()]));
3,570✔
176

177
                l.evaluate_cmp_with_literal(&r)
3,570✔
178
            }
179
            (Evaluated::StrSlice { source, range }, Evaluated::Literal(l)) => {
3,570✔
180
                let r = Literal::Text(Cow::Borrowed(&source[range.clone()]));
3,570✔
181

182
                l.evaluate_cmp(&r).map(|o| o.reverse())
3,570✔
183
            }
184
            (Evaluated::StrSlice { source, range }, Evaluated::Value(r)) => {
3,570✔
185
                let l = Literal::Text(Cow::Borrowed(&source[range.clone()]));
3,570✔
186

187
                r.evaluate_cmp_with_literal(&l).map(|o| o.reverse())
3,570✔
188
            }
189
            (
190
                Evaluated::StrSlice {
191
                    source: a,
3,570✔
192
                    range: ar,
3,570✔
193
                },
194
                Evaluated::StrSlice {
195
                    source: b,
3,570✔
196
                    range: br,
3,570✔
197
                },
198
            ) => a[ar.clone()].partial_cmp(&b[br.clone()]),
3,570✔
199
        }
200
    }
743,750✔
201

202
    pub fn add<'b>(&'a self, other: &Evaluated<'b>) -> Result<Evaluated<'b>> {
197,625✔
203
        binary_op(
197,625✔
204
            self,
197,625✔
205
            other,
197,625✔
206
            BinaryOperator::Plus,
197,625✔
207
            |l, r| l.add(r),
152,320✔
208
            |l, r| l.add(r),
45,305✔
209
        )
210
    }
197,625✔
211

212
    pub fn subtract<'b>(&'a self, other: &Evaluated<'b>) -> Result<Evaluated<'b>> {
116,790✔
213
        binary_op(
116,790✔
214
            self,
116,790✔
215
            other,
116,790✔
216
            BinaryOperator::Minus,
116,790✔
217
            |l, r| l.subtract(r),
98,940✔
218
            |l, r| l.subtract(r),
16,660✔
219
        )
220
    }
116,790✔
221

222
    pub fn multiply<'b>(&'a self, other: &Evaluated<'b>) -> Result<Evaluated<'b>> {
99,620✔
223
        binary_op(
99,620✔
224
            self,
99,620✔
225
            other,
99,620✔
226
            BinaryOperator::Multiply,
99,620✔
227
            |l, r| l.multiply(r),
78,115✔
228
            |l, r| l.multiply(r),
21,505✔
229
        )
230
    }
99,620✔
231

232
    pub fn divide<'b>(&'a self, other: &Evaluated<'b>) -> Result<Evaluated<'b>> {
81,005✔
233
        binary_op(
81,005✔
234
            self,
81,005✔
235
            other,
81,005✔
236
            BinaryOperator::Divide,
81,005✔
237
            |l, r| l.divide(r),
65,450✔
238
            |l, r| l.divide(r),
15,555✔
239
        )
240
    }
81,005✔
241

242
    pub fn bitwise_and<'b>(&'a self, other: &Evaluated<'b>) -> Result<Evaluated<'b>> {
8,330✔
243
        binary_op(
8,330✔
244
            self,
8,330✔
245
            other,
8,330✔
246
            BinaryOperator::BitwiseAnd,
8,330✔
247
            |l, r| l.bitwise_and(r),
4,760✔
248
            |l, r| l.bitwise_and(r),
3,570✔
249
        )
250
    }
8,330✔
251

252
    pub fn modulo<'b>(&'a self, other: &Evaluated<'b>) -> Result<Evaluated<'b>> {
91,630✔
253
        binary_op(
91,630✔
254
            self,
91,630✔
255
            other,
91,630✔
256
            BinaryOperator::Modulo,
91,630✔
257
            |l, r| l.modulo(r),
82,110✔
258
            |l, r| l.modulo(r),
9,520✔
259
        )
260
    }
91,630✔
261

262
    pub fn bitwise_shift_left<'b>(&'a self, other: &Evaluated<'b>) -> Result<Evaluated<'b>> {
5,950✔
263
        binary_op(
5,950✔
264
            self,
5,950✔
265
            other,
5,950✔
266
            BinaryOperator::BitwiseShiftLeft,
5,950✔
267
            |l, r| l.bitwise_shift_left(r),
5,950✔
268
            |l, r| l.bitwise_shift_left(r),
×
269
        )
270
    }
5,950✔
271

272
    pub fn bitwise_shift_right<'b>(&'a self, other: &Evaluated<'b>) -> Result<Evaluated<'b>> {
5,950✔
273
        binary_op(
5,950✔
274
            self,
5,950✔
275
            other,
5,950✔
276
            BinaryOperator::BitwiseShiftRight,
5,950✔
277
            |l, r| l.bitwise_shift_right(r),
5,950✔
278
            |l, r| l.bitwise_shift_right(r),
×
279
        )
280
    }
5,950✔
281

282
    pub fn arrow<'b>(&'a self, other: &Evaluated<'b>) -> Result<Evaluated<'b>> {
48,790✔
283
        let selector = Value::try_from(other.clone())?;
48,790✔
284

285
        if selector.is_null() {
48,790✔
NEW
286
            return Ok(Evaluated::Value(Value::Null));
×
287
        }
48,790✔
288

289
        let value_result = if let Evaluated::Value(base) = self {
48,790✔
290
            function::select_arrow_value(base, &selector)
45,220✔
291
        } else {
292
            let base = Value::try_from(self.clone())?;
3,570✔
293
            function::select_arrow_value(&base, &selector)
3,570✔
294
        };
295

296
        value_result.map(Evaluated::Value)
48,790✔
297
    }
48,790✔
298

299
    pub fn unary_plus(&self) -> Result<Evaluated<'a>> {
56,015✔
300
        match self {
56,015✔
301
            Evaluated::Literal(v) => v.unary_plus().map(Evaluated::Literal),
46,495✔
302
            Evaluated::Value(v) => v.unary_plus().map(Evaluated::Value),
8,330✔
303
            Evaluated::StrSlice { source, range } => {
1,190✔
304
                Err(EvaluateError::UnsupportedUnaryPlus(source[range.clone()].to_owned()).into())
1,190✔
305
            }
306
        }
307
    }
56,015✔
308

309
    pub fn unary_minus(&self) -> Result<Evaluated<'a>> {
221,935✔
310
        match self {
221,935✔
311
            Evaluated::Literal(v) => v.unary_minus().map(Evaluated::Literal),
212,075✔
312
            Evaluated::Value(v) => v.unary_minus().map(Evaluated::Value),
8,670✔
313
            Evaluated::StrSlice { source, range } => {
1,190✔
314
                Err(EvaluateError::UnsupportedUnaryMinus(source[range.clone()].to_owned()).into())
1,190✔
315
            }
316
        }
317
    }
221,935✔
318

319
    pub fn unary_not(self) -> Result<Evaluated<'a>> {
19,040✔
320
        if self.is_null() {
19,040✔
321
            Ok(self)
1,190✔
322
        } else {
323
            self.try_into()
17,850✔
324
                .map(|v: bool| Evaluated::Value(Value::Bool(!v)))
17,850✔
325
        }
326
    }
19,040✔
327

328
    pub fn unary_factorial(&self) -> Result<Evaluated<'a>> {
15,470✔
329
        match self {
15,470✔
330
            Evaluated::Literal(v) => Value::try_from(v).and_then(|v| v.unary_factorial()),
5,950✔
331
            Evaluated::Value(v) => v.unary_factorial(),
8,330✔
332
            Evaluated::StrSlice { source, range } => Err(EvaluateError::UnsupportedUnaryFactorial(
1,190✔
333
                source[range.clone()].to_owned(),
1,190✔
334
            )
1,190✔
335
            .into()),
1,190✔
336
        }
337
        .map(Evaluated::Value)
15,470✔
338
    }
15,470✔
339

340
    pub fn unary_bitwise_not(&self) -> Result<Evaluated<'a>> {
17,850✔
341
        match self {
17,850✔
342
            Evaluated::Literal(v) => Value::try_from(v).and_then(|v| v.unary_bitwise_not()),
4,760✔
343
            Evaluated::Value(v) => v.unary_bitwise_not(),
11,900✔
344
            Evaluated::StrSlice { source, range } => {
1,190✔
345
                Err(EvaluateError::IncompatibleUnaryBitwiseNotOperation(
1,190✔
346
                    source[range.clone()].to_owned(),
1,190✔
347
                )
1,190✔
348
                .into())
1,190✔
349
            }
350
        }
351
        .map(Evaluated::Value)
17,850✔
352
    }
17,850✔
353

354
    pub fn cast(self, data_type: &DataType) -> Result<Evaluated<'a>> {
197,467✔
355
        match self {
197,467✔
356
            Evaluated::Literal(literal) => Value::try_cast_from_literal(data_type, &literal),
155,987✔
357
            Evaluated::Value(value) => value.cast(data_type),
40,290✔
358
            Evaluated::StrSlice { source, range } => {
1,190✔
359
                Value::Str(source[range].to_owned()).cast(data_type)
1,190✔
360
            }
361
        }
362
        .map(Evaluated::Value)
197,467✔
363
    }
197,467✔
364

365
    pub fn concat(self, other: Evaluated) -> Result<Evaluated<'a>> {
52,700✔
366
        let evaluated = match (self, other) {
52,700✔
367
            (Evaluated::Literal(l), Evaluated::Literal(r)) => Evaluated::Literal(l.concat(r)),
16,660✔
368
            (Evaluated::Literal(l), Evaluated::Value(r)) => {
8,330✔
369
                Evaluated::Value((Value::try_from(l)?).concat(r))
8,330✔
370
            }
371
            (Evaluated::Value(l), Evaluated::Literal(r)) => {
9,520✔
372
                Evaluated::Value(l.concat(Value::try_from(r)?))
9,520✔
373
            }
374
            (Evaluated::Value(l), Evaluated::Value(r)) => Evaluated::Value(l.concat(r)),
12,240✔
375
            (Evaluated::Literal(l), Evaluated::StrSlice { source, range }) => {
1,190✔
376
                Evaluated::Value((Value::try_from(l)?).concat(Value::Str(source[range].to_owned())))
1,190✔
377
            }
378
            (Evaluated::Value(l), Evaluated::StrSlice { source, range }) => {
1,190✔
379
                Evaluated::Value(l.concat(Value::Str(source[range].to_owned())))
1,190✔
380
            }
381
            (Evaluated::StrSlice { source, range }, Evaluated::Literal(r)) => {
1,190✔
382
                Evaluated::Value(Value::Str(source[range].to_owned()).concat(Value::try_from(r)?))
1,190✔
383
            }
384
            (Evaluated::StrSlice { source, range }, Evaluated::Value(r)) => {
1,190✔
385
                Evaluated::Value(Value::Str(source[range].to_owned()).concat(r))
1,190✔
386
            }
387
            (
388
                Evaluated::StrSlice {
389
                    source: a,
1,190✔
390
                    range: ar,
1,190✔
391
                },
392
                Evaluated::StrSlice {
393
                    source: b,
1,190✔
394
                    range: br,
1,190✔
395
                },
396
            ) => {
397
                Evaluated::Value(Value::Str(a[ar].to_owned()).concat(Value::Str(b[br].to_owned())))
1,190✔
398
            }
399
        };
400

401
        Ok(evaluated)
52,700✔
402
    }
52,700✔
403

404
    pub fn like(&self, other: Evaluated<'a>, case_sensitive: bool) -> Result<Evaluated<'a>> {
155,890✔
405
        let evaluated = match (self, other) {
155,890✔
406
            (Evaluated::Literal(l), Evaluated::Literal(r)) => {
26,180✔
407
                Evaluated::Literal(l.like(&r, case_sensitive)?)
26,180✔
408
            }
409
            (Evaluated::Literal(l), Evaluated::Value(r)) => {
×
410
                Evaluated::Value((Value::try_from(l)?).like(&r, case_sensitive)?)
×
411
            }
412
            (Evaluated::Value(l), Evaluated::Literal(r)) => {
94,010✔
413
                Evaluated::Value(l.like(&Value::try_from(r)?, case_sensitive)?)
94,010✔
414
            }
415
            (Evaluated::Value(l), Evaluated::Value(r)) => {
×
416
                Evaluated::Value(l.like(&r, case_sensitive)?)
×
417
            }
418
            (Evaluated::Literal(l), Evaluated::StrSlice { source, range }) => Evaluated::Value(
5,950✔
419
                Value::try_from(l)?.like(&Value::Str(source[range].to_owned()), case_sensitive)?,
5,950✔
420
            ),
421
            (Evaluated::StrSlice { source, range }, Evaluated::Literal(r)) => Evaluated::Value(
5,950✔
422
                Value::Str(source[range.clone()].to_owned())
5,950✔
423
                    .like(&Value::try_from(r)?, case_sensitive)?,
5,950✔
424
            ),
425
            (
426
                Evaluated::StrSlice {
427
                    source: a,
11,900✔
428
                    range: ar,
11,900✔
429
                },
430
                Evaluated::StrSlice {
431
                    source: b,
11,900✔
432
                    range: br,
11,900✔
433
                },
434
            ) => Evaluated::Value(
435
                Value::Str(a[ar.clone()].to_owned())
11,900✔
436
                    .like(&Value::Str(b[br].to_owned()), case_sensitive)?,
11,900✔
437
            ),
438
            (Evaluated::StrSlice { source, range }, Evaluated::Value(r)) => Evaluated::Value(
5,950✔
439
                Value::Str(source[range.clone()].to_owned()).like(&r, case_sensitive)?,
5,950✔
440
            ),
441
            (Evaluated::Value(l), Evaluated::StrSlice { source, range }) => {
5,950✔
442
                Evaluated::Value(l.like(&Value::Str(source[range].to_owned()), case_sensitive)?)
5,950✔
443
            }
444
        };
445

446
        Ok(evaluated)
151,130✔
447
    }
155,890✔
448

449
    pub fn ltrim(self, name: String, chars: Option<Evaluated<'_>>) -> Result<Evaluated<'a>> {
23,800✔
450
        let (source, range) = match self {
19,040✔
451
            Evaluated::Literal(Literal::Text(l)) => {
9,520✔
452
                let end = l.len();
9,520✔
453
                (l, 0..end)
9,520✔
454
            }
455
            Evaluated::Literal(Literal::Null) | Evaluated::Value(Value::Null) => {
456
                return Ok(Evaluated::Value(Value::Null));
3,570✔
457
            }
458
            Evaluated::StrSlice { source, range } => (source, range),
3,570✔
459
            Evaluated::Value(Value::Str(v)) => {
5,950✔
460
                let end = v.len();
5,950✔
461
                (Cow::Owned(v), 0..end)
5,950✔
462
            }
463
            _ => return Err(EvaluateError::FunctionRequiresStringValue(name).into()),
1,190✔
464
        };
465

466
        let filter_chars = match chars {
19,040✔
467
            Some(expr) => match expr.try_into()? {
11,900✔
468
                Value::Str(value) => value,
9,520✔
469
                Value::Null => {
470
                    return Ok(Evaluated::Value(Value::Null));
1,190✔
471
                }
472
                _ => {
473
                    return Err(EvaluateError::FunctionRequiresStringValue(name).into());
1,190✔
474
                }
475
            }
476
            .chars()
9,520✔
477
            .collect::<Vec<_>>(),
9,520✔
478
            None => vec![' '],
7,140✔
479
        };
480
        let sliced_expr = &source[range.clone()];
16,660✔
481
        let matched_vec: Vec<_> = sliced_expr.match_indices(&filter_chars[..]).collect();
16,660✔
482

483
        //"x".trim_start_matches(['x','y','z']) => ""
484
        if matched_vec.len() == sliced_expr.len() {
16,660✔
485
            return Ok(Evaluated::StrSlice {
2,380✔
486
                source,
2,380✔
487
                range: 0..0,
2,380✔
488
            });
2,380✔
489
        }
14,280✔
490
        //"tuv".trim_start_matches(['x','y','z']) => "tuv"
491
        if matched_vec.is_empty() {
14,280✔
492
            return Ok(Evaluated::StrSlice { source, range });
3,570✔
493
        }
10,710✔
494
        //"txu".trim_start_matches(['x','y','z']) => "txu"
495
        if matched_vec[0].0 != 0 && matched_vec[matched_vec.len() - 1].0 != sliced_expr.len() - 1 {
10,710✔
496
            return Ok(Evaluated::StrSlice { source, range });
2,380✔
497
        }
8,330✔
498
        let pivot = matched_vec
8,330✔
499
            .iter()
8,330✔
500
            .enumerate()
8,330✔
501
            .skip_while(|(vec_idx, (slice_idx, _))| vec_idx == slice_idx)
21,420✔
502
            .map(|(vec_idx, (_, _))| vec_idx)
8,330✔
503
            .next();
8,330✔
504

505
        let start = match pivot {
8,330✔
506
            Some(idx) => match idx {
4,760✔
507
                0 => 0,
2,380✔
508
                _ => matched_vec[idx - 1].0 + 1,
2,380✔
509
            },
510
            _ => matched_vec[matched_vec.len() - 1].0 + 1,
3,570✔
511
        };
512

513
        Ok(Evaluated::StrSlice {
8,330✔
514
            source,
8,330✔
515
            range: range.start + start..range.end,
8,330✔
516
        })
8,330✔
517
    }
23,800✔
518

519
    pub fn is_null(&self) -> bool {
13,832,730✔
520
        match self {
13,832,730✔
521
            Evaluated::Value(v) => v.is_null(),
11,240,995✔
522
            Evaluated::StrSlice { .. } => false,
55,930✔
523
            Evaluated::Literal(v) => matches!(v, &Literal::Null),
2,535,805✔
524
        }
525
    }
13,832,730✔
526

527
    pub fn rtrim(self, name: String, chars: Option<Evaluated<'_>>) -> Result<Evaluated<'a>> {
28,560✔
528
        let (source, range) = match self {
23,800✔
529
            Evaluated::Literal(Literal::Text(l)) => {
15,470✔
530
                let end = l.len();
15,470✔
531
                (l, 0..end)
15,470✔
532
            }
533
            Evaluated::Literal(Literal::Null) | Evaluated::Value(Value::Null) => {
534
                return Ok(Evaluated::Value(Value::Null));
3,570✔
535
            }
536
            Evaluated::StrSlice { source, range } => (source, range),
2,380✔
537
            Evaluated::Value(Value::Str(v)) => {
5,950✔
538
                let end = v.len();
5,950✔
539
                (Cow::Owned(v), 0..end)
5,950✔
540
            }
541
            _ => return Err(EvaluateError::FunctionRequiresStringValue(name).into()),
1,190✔
542
        };
543

544
        let filter_chars = match chars {
23,800✔
545
            Some(expr) => match expr.try_into()? {
19,040✔
546
                Value::Str(value) => value,
16,660✔
547
                Value::Null => {
548
                    return Ok(Evaluated::Value(Value::Null));
1,190✔
549
                }
550
                _ => {
551
                    return Err(EvaluateError::FunctionRequiresStringValue(name).into());
1,190✔
552
                }
553
            }
554
            .chars()
16,660✔
555
            .collect::<Vec<_>>(),
16,660✔
556
            None => vec![' '],
4,760✔
557
        };
558
        let sliced_expr = &source[range.clone()];
21,420✔
559
        let matched_vec: Vec<_> = sliced_expr.match_indices(&filter_chars[..]).collect();
21,420✔
560

561
        //"x".trim_end_matches(['x','y','z']) => ""
562
        if matched_vec.len() == sliced_expr.len() {
21,420✔
563
            return Ok(Evaluated::StrSlice {
2,380✔
564
                source,
2,380✔
565
                range: 0..0,
2,380✔
566
            });
2,380✔
567
        }
19,040✔
568
        //"tuv".trim_end_matches(['x','y','z']) => "tuv"
569
        if matched_vec.is_empty() {
19,040✔
570
            return Ok(Evaluated::StrSlice { source, range });
3,570✔
571
        }
15,470✔
572
        //"txu".trim_end_matches(['x','y','z']) => "txu"
573
        if matched_vec[0].0 != 0 && matched_vec[matched_vec.len() - 1].0 != sliced_expr.len() - 1 {
15,470✔
574
            return Ok(Evaluated::StrSlice { source, range });
2,380✔
575
        }
13,090✔
576

577
        let pivot = matched_vec
13,090✔
578
            .iter()
13,090✔
579
            .rev()
13,090✔
580
            .enumerate()
13,090✔
581
            .skip_while(|(vec_idx, (slice_idx, _))| *vec_idx == sliced_expr.len() - slice_idx - 1)
30,940✔
582
            .map(|(vec_idx, (_, _))| vec_idx)
13,090✔
583
            .next();
13,090✔
584

585
        let end = match pivot {
13,090✔
586
            Some(idx) => match idx {
4,760✔
587
                0 => range.end,
2,380✔
588
                _ => matched_vec[matched_vec.len() - idx].0,
2,380✔
589
            },
590
            _ => matched_vec[0].0,
8,330✔
591
        };
592

593
        Ok(Evaluated::StrSlice {
13,090✔
594
            source,
13,090✔
595
            range: range.start..end,
13,090✔
596
        })
13,090✔
597
    }
28,560✔
598

599
    pub fn substr(
147,561✔
600
        self,
147,561✔
601
        name: String,
147,561✔
602
        start: Evaluated<'a>,
147,561✔
603
        count: Option<Evaluated<'a>>,
147,561✔
604
    ) -> Result<Evaluated<'a>> {
147,561✔
605
        let (source, range) = match self {
145,181✔
606
            Evaluated::Literal(Literal::Text(l)) => {
67,831✔
607
                let end = l.len();
67,831✔
608
                (l, 0..end)
67,831✔
609
            }
610
            Evaluated::Literal(Literal::Null) | Evaluated::Value(Value::Null) => {
611
                return Ok(Evaluated::Value(Value::Null));
1,190✔
612
            }
613
            Evaluated::StrSlice { source, range } => (source, range),
7,140✔
614
            Evaluated::Value(Value::Str(v)) => {
70,210✔
615
                let end = v.len();
70,210✔
616
                (Cow::Owned(v), 0..end)
70,210✔
617
            }
618
            _ => return Err(EvaluateError::FunctionRequiresStringValue(name).into()),
1,190✔
619
        };
620

621
        let start = {
142,801✔
622
            let value = start.try_into()?;
145,181✔
623
            match value {
145,181✔
624
                Value::I64(num) => num,
142,801✔
625
                _ => return exceptional_int_val_to_eval(name, value),
2,380✔
626
            }
627
        } - 1;
628

629
        let count = match count {
142,801✔
630
            Some(eval) => {
58,311✔
631
                let value = eval.try_into()?;
58,311✔
632
                match value {
58,311✔
633
                    Value::I64(num) => num,
57,121✔
634
                    _ => return exceptional_int_val_to_eval(name, value),
1,190✔
635
                }
636
            }
637
            None => source.len() as i64,
84,490✔
638
        };
639

640
        let end = if count < 0 {
141,611✔
641
            return Err(EvaluateError::NegativeSubstrLenNotAllowed.into());
1,190✔
642
        } else {
643
            (range.start as i64 + start + count).clamp(0, source.len() as i64) as usize
140,421✔
644
        };
645

646
        let start = (start + range.start as i64).clamp(0, source.len() as i64) as usize;
140,421✔
647

648
        Ok(Evaluated::StrSlice {
140,421✔
649
            source,
140,421✔
650
            range: start..end,
140,421✔
651
        })
140,421✔
652
    }
147,561✔
653

654
    pub fn trim(
47,600✔
655
        self,
47,600✔
656
        name: String,
47,600✔
657
        filter_chars: Option<Evaluated<'_>>,
47,600✔
658
        trim_where_field: &Option<TrimWhereField>,
47,600✔
659
    ) -> Result<Evaluated<'a>> {
47,600✔
660
        let (source, range) = match self {
40,460✔
661
            Evaluated::Literal(Literal::Text(l)) => {
11,900✔
662
                let end = l.len();
11,900✔
663
                (l, 0..end)
11,900✔
664
            }
665
            Evaluated::Literal(Literal::Null) | Evaluated::Value(Value::Null) => {
666
                return Ok(Evaluated::Value(Value::Null));
4,760✔
667
            }
668
            Evaluated::StrSlice { source, range } => (source, range),
2,380✔
669
            Evaluated::Value(Value::Str(v)) => {
26,180✔
670
                let end = v.len();
26,180✔
671
                (Cow::Owned(v), 0..end)
26,180✔
672
            }
673
            _ => return Err(EvaluateError::FunctionRequiresStringValue(name).into()),
2,380✔
674
        };
675

676
        let filter_chars = match filter_chars {
40,460✔
677
            Some(expr) => match expr.try_into()? {
28,560✔
678
                Value::Str(value) => value,
26,180✔
679
                Value::Null => {
680
                    return Ok(Evaluated::Value(Value::Null));
1,190✔
681
                }
682
                _ => {
683
                    return Err(EvaluateError::FunctionRequiresStringValue(name).into());
1,190✔
684
                }
685
            }
686
            .chars()
26,180✔
687
            .collect::<Vec<_>>(),
26,180✔
688
            None => vec![' '],
11,900✔
689
        };
690
        let sliced_expr = &source[range.clone()];
38,080✔
691
        let matched_vec: Vec<_> = sliced_expr.match_indices(&filter_chars[..]).collect();
38,080✔
692
        //filter_chars => ['x','y','z']
693
        //"x".trim_matches(filter_chars[..]) => ""
694
        if matched_vec.len() == sliced_expr.len() {
38,080✔
695
            return Ok(Evaluated::StrSlice {
2,380✔
696
                source,
2,380✔
697
                range: 0..0,
2,380✔
698
            });
2,380✔
699
        }
35,700✔
700
        //filter_chars => ['x','y','z']
701
        //"tuv".trim_matches(filter_chars[..]) => "tuv"
702
        if matched_vec.is_empty() {
35,700✔
703
            return Ok(Evaluated::StrSlice { source, range });
4,760✔
704
        }
30,940✔
705
        //filter_chars => ['x','y','z']
706
        //"txu".trim_matches(filter_chars[..]) => "txu"
707
        if matched_vec[0].0 != 0 && matched_vec[matched_vec.len() - 1].0 != sliced_expr.len() - 1 {
30,940✔
708
            return Ok(Evaluated::StrSlice { source, range });
1,190✔
709
        }
29,750✔
710
        match trim_where_field {
23,800✔
711
            Some(TrimWhereField::Both) => {
712
                //filter_chars => ['x','y','z']
713
                //"xyzbyxlxyz  ".trim_matches(filter_chars[..]) => "byxlxyz  "
714
                if matched_vec[0].0 == 0
8,330✔
715
                    && matched_vec[matched_vec.len() - 1].0 != sliced_expr.len() - 1
5,950✔
716
                {
717
                    let pivot = matched_vec
2,380✔
718
                        .iter()
2,380✔
719
                        .enumerate()
2,380✔
720
                        .skip_while(|(vec_idx, (slice_idx, _))| vec_idx == slice_idx)
13,090✔
721
                        .map(|(vec_idx, (_, _))| vec_idx)
2,380✔
722
                        .next();
2,380✔
723

724
                    let start = match pivot {
2,380✔
725
                        Some(idx) => matched_vec[idx - 1].0 + 1,
1,190✔
726
                        _ => matched_vec[matched_vec.len() - 1].0 + 1,
1,190✔
727
                    };
728

729
                    return Ok(Evaluated::StrSlice {
2,380✔
730
                        source,
2,380✔
731
                        range: range.start + start..range.end,
2,380✔
732
                    });
2,380✔
733
                }
5,950✔
734
                //filter_chars => ['x','y','z']
735
                //"  xyzblankxyzxx".trim_matches(filter_chars[..]) => "  xyzblank"
736
                if matched_vec[0].0 != 0
5,950✔
737
                    && matched_vec[matched_vec.len() - 1].0 == sliced_expr.len() - 1
2,380✔
738
                {
739
                    let pivot = matched_vec
2,380✔
740
                        .iter()
2,380✔
741
                        .rev()
2,380✔
742
                        .enumerate()
2,380✔
743
                        .skip_while(|(vec_idx, (slice_idx, _))| {
13,090✔
744
                            *vec_idx == sliced_expr.len() - slice_idx - 1
13,090✔
745
                        })
13,090✔
746
                        .map(|(vec_idx, (_, _))| vec_idx)
2,380✔
747
                        .next();
2,380✔
748

749
                    let end = match pivot {
2,380✔
750
                        Some(idx) => matched_vec[matched_vec.len() - idx].0,
1,190✔
751
                        _ => matched_vec[0].0,
1,190✔
752
                    };
753

754
                    return Ok(Evaluated::StrSlice {
2,380✔
755
                        source,
2,380✔
756
                        range: range.start..end,
2,380✔
757
                    });
2,380✔
758
                }
3,570✔
759
                //filter_chars => ['x','y','z']
760
                //"xxbyz".trim_matches(filter_chars[..]) => "b"
761
                let pivot = matched_vec
3,570✔
762
                    .iter()
3,570✔
763
                    .enumerate()
3,570✔
764
                    .skip_while(|(vec_idx, (slice_idx, _))| vec_idx == slice_idx)
13,090✔
765
                    .map(|(vec_idx, (_, _))| vec_idx)
3,570✔
766
                    .next()
3,570✔
767
                    .unwrap_or(0);
3,570✔
768

769
                let trim_range = matched_vec[pivot - 1].0..(matched_vec[pivot].0 + range.start);
3,570✔
770

771
                Ok(Evaluated::StrSlice {
3,570✔
772
                    source,
3,570✔
773
                    range: range.start + trim_range.start + 1..trim_range.end,
3,570✔
774
                })
3,570✔
775
            }
776
            Some(TrimWhereField::Leading) => {
777
                let pivot = matched_vec
8,330✔
778
                    .iter()
8,330✔
779
                    .enumerate()
8,330✔
780
                    .skip_while(|(vec_idx, (slice_idx, _))| vec_idx == slice_idx)
28,560✔
781
                    .map(|(vec_idx, (_, _))| vec_idx)
8,330✔
782
                    .next();
8,330✔
783

784
                let start = match pivot {
8,330✔
785
                    Some(idx) => match idx {
5,950✔
786
                        0 => 0,
2,380✔
787
                        _ => matched_vec[idx - 1].0 + 1,
3,570✔
788
                    },
789
                    _ => matched_vec[matched_vec.len() - 1].0 + 1,
2,380✔
790
                };
791

792
                Ok(Evaluated::StrSlice {
8,330✔
793
                    source,
8,330✔
794
                    range: range.start + start..range.end,
8,330✔
795
                })
8,330✔
796
            }
797
            Some(TrimWhereField::Trailing) => {
798
                let pivot = matched_vec
7,140✔
799
                    .iter()
7,140✔
800
                    .rev()
7,140✔
801
                    .enumerate()
7,140✔
802
                    .skip_while(|(vec_idx, (slice_idx, _))| {
26,180✔
803
                        *vec_idx == sliced_expr.len() - slice_idx - 1
26,180✔
804
                    })
26,180✔
805
                    .map(|(vec_idx, (_, _))| vec_idx)
7,140✔
806
                    .next();
7,140✔
807

808
                let end = match pivot {
7,140✔
809
                    Some(idx) => match idx {
5,950✔
810
                        0 => range.end,
2,380✔
811
                        _ => matched_vec[matched_vec.len() - idx].0,
3,570✔
812
                    },
813
                    _ => matched_vec[0].0,
1,190✔
814
                };
815

816
                Ok(Evaluated::StrSlice {
7,140✔
817
                    source,
7,140✔
818
                    range: range.start..end,
7,140✔
819
                })
7,140✔
820
            }
821
            None => {
822
                let start = source
5,950✔
823
                    .chars()
5,950✔
824
                    .skip(range.start)
5,950✔
825
                    .enumerate()
5,950✔
826
                    .find(|(_, c)| !c.is_whitespace())
22,610✔
827
                    .map(|(idx, _)| idx + range.start)
5,950✔
828
                    .unwrap_or(0);
5,950✔
829

830
                let end = source.len()
5,950✔
831
                    - source
5,950✔
832
                        .chars()
5,950✔
833
                        .rev()
5,950✔
834
                        .skip(source.len() - range.end)
5,950✔
835
                        .enumerate()
5,950✔
836
                        .find(|(_, c)| !c.is_whitespace())
20,230✔
837
                        .map(|(idx, _)| source.len() - (range.end - idx))
5,950✔
838
                        .unwrap_or(0);
5,950✔
839

840
                Ok(Evaluated::StrSlice {
5,950✔
841
                    source,
5,950✔
842
                    range: start..end,
5,950✔
843
                })
5,950✔
844
            }
845
        }
846
    }
47,600✔
847

848
    pub fn try_into_value(self, data_type: &DataType, nullable: bool) -> Result<Value> {
2,063,205✔
849
        let value = match self {
2,063,205✔
850
            Evaluated::Literal(v) => Value::try_from_literal(data_type, &v)?,
1,944,715✔
851
            Evaluated::Value(v) => v,
111,350✔
852
            Evaluated::StrSlice {
853
                source: s,
7,140✔
854
                range: r,
7,140✔
855
            } => Value::Str(s[r].to_owned()),
7,140✔
856
        };
857

858
        value.validate_null(nullable)?;
2,020,365✔
859

860
        Ok(value)
2,016,795✔
861
    }
2,063,205✔
862
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc