1use crate::ast::{CompareOperator, Expr, Value};
11use std::marker::PhantomData;
12
13pub trait Schema {
40 type Field: Copy + Eq;
42
43 fn field_name(field: Self::Field) -> &'static str;
45}
46
47pub struct FieldRef<S: Schema, T> {
61 field: S::Field,
62 _phantom: PhantomData<(S, T)>,
63}
64
65impl<S: Schema, T> FieldRef<S, T> {
66 #[must_use]
74 pub const fn new(field: S::Field) -> Self {
75 Self {
76 field,
77 _phantom: PhantomData,
78 }
79 }
80
81 #[must_use]
83 pub fn name(&self) -> &'static str {
84 S::field_name(self.field)
85 }
86
87 #[must_use]
89 fn identifier(&self) -> Expr {
90 Expr::Identifier(self.name().to_owned())
91 }
92}
93
94impl<S: Schema, T> Clone for FieldRef<S, T> {
95 fn clone(&self) -> Self {
96 *self
97 }
98}
99
100impl<S: Schema, T> Copy for FieldRef<S, T> {}
101
102impl<S: Schema, T> std::fmt::Debug for FieldRef<S, T> {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 f.debug_struct("FieldRef")
105 .field("field", &self.name())
106 .finish()
107 }
108}
109
110impl<S: Schema, T> PartialEq for FieldRef<S, T> {
111 fn eq(&self, other: &Self) -> bool {
112 self.field == other.field
113 }
114}
115
116impl<S: Schema, T> Eq for FieldRef<S, T> {}
117
118impl<S: Schema, T> std::hash::Hash for FieldRef<S, T>
119where
120 S::Field: std::hash::Hash,
121{
122 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
123 self.field.hash(state);
124 }
125}
126
127#[doc(hidden)]
132pub trait AsFieldName {
133 fn as_field_name(&self) -> &'static str;
135}
136
137#[doc(hidden)]
143pub trait AsFieldKey<S: Schema> {
144 fn as_field_key(&self) -> S::Field;
146}
147
148impl<S: Schema, T> AsFieldName for FieldRef<S, T> {
149 fn as_field_name(&self) -> &'static str {
150 self.name()
151 }
152}
153
154impl<S: Schema, T> AsFieldKey<S> for FieldRef<S, T> {
155 fn as_field_key(&self) -> S::Field {
156 self.field
157 }
158}
159
160impl<T: AsFieldName + ?Sized> AsFieldName for &T {
161 fn as_field_name(&self) -> &'static str {
162 (*self).as_field_name()
163 }
164}
165
166impl<S: Schema, T: AsFieldKey<S> + ?Sized> AsFieldKey<S> for &T {
167 fn as_field_key(&self) -> S::Field {
168 (*self).as_field_key()
169 }
170}
171
172pub trait IntoODataValue {
174 fn into_odata_value(self) -> Value;
176}
177
178impl IntoODataValue for bool {
179 fn into_odata_value(self) -> Value {
180 Value::Bool(self)
181 }
182}
183
184impl IntoODataValue for uuid::Uuid {
185 fn into_odata_value(self) -> Value {
186 Value::Uuid(self)
187 }
188}
189
190impl IntoODataValue for String {
191 fn into_odata_value(self) -> Value {
192 Value::String(self)
193 }
194}
195
196impl IntoODataValue for &str {
197 fn into_odata_value(self) -> Value {
198 Value::String(self.to_owned())
199 }
200}
201
202impl IntoODataValue for i32 {
203 fn into_odata_value(self) -> Value {
204 Value::Number(self.into())
205 }
206}
207
208impl IntoODataValue for i64 {
209 fn into_odata_value(self) -> Value {
210 Value::Number(self.into())
211 }
212}
213
214impl IntoODataValue for u32 {
215 fn into_odata_value(self) -> Value {
216 Value::Number(self.into())
217 }
218}
219
220impl IntoODataValue for u64 {
221 fn into_odata_value(self) -> Value {
222 Value::Number(self.into())
223 }
224}
225
226impl IntoODataValue for chrono::DateTime<chrono::Utc> {
227 fn into_odata_value(self) -> Value {
228 Value::DateTime(self)
229 }
230}
231
232impl IntoODataValue for chrono::NaiveDate {
233 fn into_odata_value(self) -> Value {
234 Value::Date(self)
235 }
236}
237
238impl IntoODataValue for chrono::NaiveTime {
239 fn into_odata_value(self) -> Value {
240 Value::Time(self)
241 }
242}
243
244impl<S: Schema, T> FieldRef<S, T> {
246 #[must_use]
254 pub fn eq<V: IntoODataValue>(self, value: V) -> Expr {
255 Expr::Compare(
256 Box::new(self.identifier()),
257 CompareOperator::Eq,
258 Box::new(Expr::Value(value.into_odata_value())),
259 )
260 }
261
262 #[must_use]
264 pub fn ne<V: IntoODataValue>(self, value: V) -> Expr {
265 Expr::Compare(
266 Box::new(self.identifier()),
267 CompareOperator::Ne,
268 Box::new(Expr::Value(value.into_odata_value())),
269 )
270 }
271
272 #[must_use]
274 pub fn gt<V: IntoODataValue>(self, value: V) -> Expr {
275 Expr::Compare(
276 Box::new(self.identifier()),
277 CompareOperator::Gt,
278 Box::new(Expr::Value(value.into_odata_value())),
279 )
280 }
281
282 #[must_use]
284 pub fn ge<V: IntoODataValue>(self, value: V) -> Expr {
285 Expr::Compare(
286 Box::new(self.identifier()),
287 CompareOperator::Ge,
288 Box::new(Expr::Value(value.into_odata_value())),
289 )
290 }
291
292 #[must_use]
294 pub fn lt<V: IntoODataValue>(self, value: V) -> Expr {
295 Expr::Compare(
296 Box::new(self.identifier()),
297 CompareOperator::Lt,
298 Box::new(Expr::Value(value.into_odata_value())),
299 )
300 }
301
302 #[must_use]
304 pub fn le<V: IntoODataValue>(self, value: V) -> Expr {
305 Expr::Compare(
306 Box::new(self.identifier()),
307 CompareOperator::Le,
308 Box::new(Expr::Value(value.into_odata_value())),
309 )
310 }
311
312 #[must_use]
320 pub fn is_null(self) -> Expr {
321 Expr::Compare(
322 Box::new(self.identifier()),
323 CompareOperator::Eq,
324 Box::new(Expr::Value(Value::Null)),
325 )
326 }
327
328 #[must_use]
336 pub fn is_not_null(self) -> Expr {
337 Expr::Compare(
338 Box::new(self.identifier()),
339 CompareOperator::Ne,
340 Box::new(Expr::Value(Value::Null)),
341 )
342 }
343}
344
345impl<S: Schema> FieldRef<S, String> {
347 #[must_use]
355 pub fn contains(self, substring: &str) -> Expr {
356 Expr::Function(
357 "contains".to_owned(),
358 vec![
359 self.identifier(),
360 Expr::Value(Value::String(substring.to_owned())),
361 ],
362 )
363 }
364
365 #[must_use]
373 pub fn startswith(self, prefix: &str) -> Expr {
374 Expr::Function(
375 "startswith".to_owned(),
376 vec![
377 self.identifier(),
378 Expr::Value(Value::String(prefix.to_owned())),
379 ],
380 )
381 }
382
383 #[must_use]
391 pub fn endswith(self, suffix: &str) -> Expr {
392 Expr::Function(
393 "endswith".to_owned(),
394 vec![
395 self.identifier(),
396 Expr::Value(Value::String(suffix.to_owned())),
397 ],
398 )
399 }
400}