Skip to main content

icydb_core/db/query/builder/
field.rs

1use crate::{
2    db::query::predicate::{CoercionId, CompareOp, ComparePredicate, Predicate},
3    traits::FieldValue,
4    value::Value,
5};
6use derive_more::Deref;
7
8///
9/// FieldRef
10///
11/// Zero-cost wrapper around a static field name used in predicates.
12/// Enables method-based predicate builders without allocating.
13/// Carries only a `&'static str` and derefs to `str`.
14///
15
16#[derive(Clone, Copy, Deref, Eq, Hash, PartialEq)]
17pub struct FieldRef(&'static str);
18
19impl FieldRef {
20    /// Create a new field reference.
21    #[must_use]
22    pub const fn new(name: &'static str) -> Self {
23        Self(name)
24    }
25
26    /// Return the underlying field name.
27    #[must_use]
28    pub const fn as_str(self) -> &'static str {
29        self.0
30    }
31
32    // ------------------------------------------------------------------
33    // Comparison predicates
34    // ------------------------------------------------------------------
35
36    /// Strict equality comparison (no coercion).
37    #[must_use]
38    pub fn eq(self, value: impl FieldValue) -> Predicate {
39        Predicate::Compare(ComparePredicate::with_coercion(
40            self.0,
41            CompareOp::Eq,
42            value.to_value(),
43            CoercionId::Strict,
44        ))
45    }
46
47    /// Case-insensitive equality for text fields.
48    #[must_use]
49    pub fn text_eq_ci(self, value: impl FieldValue) -> Predicate {
50        Predicate::Compare(ComparePredicate::with_coercion(
51            self.0,
52            CompareOp::Eq,
53            value.to_value(),
54            CoercionId::TextCasefold,
55        ))
56    }
57
58    /// Strict inequality comparison.
59    #[must_use]
60    pub fn ne(self, value: impl FieldValue) -> Predicate {
61        Predicate::Compare(ComparePredicate::with_coercion(
62            self.0,
63            CompareOp::Ne,
64            value.to_value(),
65            CoercionId::Strict,
66        ))
67    }
68
69    /// Less-than comparison with numeric widening.
70    #[must_use]
71    pub fn lt(self, value: impl FieldValue) -> Predicate {
72        Predicate::Compare(ComparePredicate::with_coercion(
73            self.0,
74            CompareOp::Lt,
75            value.to_value(),
76            CoercionId::NumericWiden,
77        ))
78    }
79
80    /// Less-than-or-equal comparison with numeric widening.
81    #[must_use]
82    pub fn lte(self, value: impl FieldValue) -> Predicate {
83        Predicate::Compare(ComparePredicate::with_coercion(
84            self.0,
85            CompareOp::Lte,
86            value.to_value(),
87            CoercionId::NumericWiden,
88        ))
89    }
90
91    /// Greater-than comparison with numeric widening.
92    #[must_use]
93    pub fn gt(self, value: impl FieldValue) -> Predicate {
94        Predicate::Compare(ComparePredicate::with_coercion(
95            self.0,
96            CompareOp::Gt,
97            value.to_value(),
98            CoercionId::NumericWiden,
99        ))
100    }
101
102    /// Greater-than-or-equal comparison with numeric widening.
103    #[must_use]
104    pub fn gte(self, value: impl FieldValue) -> Predicate {
105        Predicate::Compare(ComparePredicate::with_coercion(
106            self.0,
107            CompareOp::Gte,
108            value.to_value(),
109            CoercionId::NumericWiden,
110        ))
111    }
112
113    /// Membership test against a fixed list (strict).
114    #[must_use]
115    pub fn in_list<I, V>(self, values: I) -> Predicate
116    where
117        I: IntoIterator<Item = V>,
118        V: FieldValue,
119    {
120        Predicate::Compare(ComparePredicate::with_coercion(
121            self.0,
122            CompareOp::In,
123            Value::List(values.into_iter().map(|v| v.to_value()).collect()),
124            CoercionId::Strict,
125        ))
126    }
127
128    // ------------------------------------------------------------------
129    // Structural predicates
130    // ------------------------------------------------------------------
131
132    /// Field is present and explicitly null.
133    #[must_use]
134    pub fn is_null(self) -> Predicate {
135        Predicate::IsNull {
136            field: self.0.to_string(),
137        }
138    }
139
140    /// Field is not present at all.
141    #[must_use]
142    pub fn is_missing(self) -> Predicate {
143        Predicate::IsMissing {
144            field: self.0.to_string(),
145        }
146    }
147
148    /// Field is present but empty (collection- or string-specific).
149    #[must_use]
150    pub fn is_empty(self) -> Predicate {
151        Predicate::IsEmpty {
152            field: self.0.to_string(),
153        }
154    }
155
156    /// Field is present and non-empty.
157    #[must_use]
158    pub fn is_not_empty(self) -> Predicate {
159        Predicate::IsNotEmpty {
160            field: self.0.to_string(),
161        }
162    }
163
164    /// Case-sensitive substring match for text fields.
165    #[must_use]
166    pub fn text_contains(self, value: impl FieldValue) -> Predicate {
167        Predicate::TextContains {
168            field: self.0.to_string(),
169            value: value.to_value(),
170        }
171    }
172
173    /// Case-insensitive substring match for text fields.
174    #[must_use]
175    pub fn text_contains_ci(self, value: impl FieldValue) -> Predicate {
176        Predicate::TextContainsCi {
177            field: self.0.to_string(),
178            value: value.to_value(),
179        }
180    }
181}
182
183// ----------------------------------------------------------------------
184// Boundary traits
185// ----------------------------------------------------------------------
186
187impl AsRef<str> for FieldRef {
188    fn as_ref(&self) -> &str {
189        self.0
190    }
191}