icydb_core/db/executor/coerce/
text.rs

1//! Text coercion layer
2//!
3//! Centralises all text-based comparison logic so FilterEvaluator and
4//! coerce_basic() don’t need to know about TextMode, folding, or the
5//! Value::text_* helpers.
6
7use crate::{
8    db::primitives::filter::Cmp,
9    value::{TextMode, Value},
10};
11
12/// Entry point used by coerce_basic().
13///
14/// Returns `None` if *either* side is not `Value::Text(_)`.
15#[must_use]
16pub fn coerce_text(left: &Value, right: &Value, cmp: Cmp) -> Option<bool> {
17    // Fast reject for non-text values (ensures purity of this layer).
18    if !left.is_text() || !right.is_text() {
19        return None;
20    }
21
22    match cmp {
23        // --- strict CS (case-sensitive) ---
24        Cmp::Eq => eq_cs(left, right),
25        Cmp::Ne => eq_cs(left, right).map(|b| !b),
26
27        Cmp::Contains => contains_cs(left, right),
28        Cmp::StartsWith => starts_with_cs(left, right),
29        Cmp::EndsWith => ends_with_cs(left, right),
30
31        // --- CI (case-insensitive) ---
32        Cmp::EqCi => eq_ci(left, right),
33        Cmp::NeCi => eq_ci(left, right).map(|b| !b),
34
35        Cmp::ContainsCi => contains_ci(left, right),
36        Cmp::StartsWithCi => starts_with_ci(left, right),
37        Cmp::EndsWithCi => ends_with_ci(left, right),
38
39        _ => None,
40    }
41}
42
43/// ----------------------
44/// Raw helpers (Value::*)
45/// ----------------------
46
47fn eq_cs(a: &Value, b: &Value) -> Option<bool> {
48    a.text_eq(b, TextMode::Cs)
49}
50
51fn contains_cs(a: &Value, b: &Value) -> Option<bool> {
52    a.text_contains(b, TextMode::Cs)
53}
54
55fn starts_with_cs(a: &Value, b: &Value) -> Option<bool> {
56    a.text_starts_with(b, TextMode::Cs)
57}
58
59fn ends_with_cs(a: &Value, b: &Value) -> Option<bool> {
60    a.text_ends_with(b, TextMode::Cs)
61}
62
63fn eq_ci(a: &Value, b: &Value) -> Option<bool> {
64    a.text_eq(b, TextMode::Ci)
65}
66
67fn contains_ci(a: &Value, b: &Value) -> Option<bool> {
68    a.text_contains(b, TextMode::Ci)
69}
70
71fn starts_with_ci(a: &Value, b: &Value) -> Option<bool> {
72    a.text_starts_with(b, TextMode::Ci)
73}
74
75fn ends_with_ci(a: &Value, b: &Value) -> Option<bool> {
76    a.text_ends_with(b, TextMode::Ci)
77}