toasty_core/stmt/
value.rs

1use super::{sparse_record::SparseRecord, Entry, EntryPath, Type, TypeUnion, ValueRecord};
2use std::cmp::Ordering;
3use std::hash::Hash;
4
5#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
6pub enum Value {
7    /// Boolean value
8    Bool(bool),
9
10    /// Signed 8-bit integer
11    I8(i8),
12
13    /// Signed 16-bit integer
14    I16(i16),
15
16    /// Signed 32-bit integer
17    I32(i32),
18
19    /// Signed 64-bit integer
20    I64(i64),
21
22    /// Unsigned 8-bit integer
23    U8(u8),
24
25    /// Unsigned 16-bit integer
26    U16(u16),
27
28    /// Unsigned 32-bit integer
29    U32(u32),
30
31    /// Unsigned 64-bit integer
32    U64(u64),
33
34    /// A typed record
35    SparseRecord(SparseRecord),
36
37    /// Null value
38    #[default]
39    Null,
40
41    /// Record value, either borrowed or owned
42    Record(ValueRecord),
43
44    /// A list of values of the same type
45    List(Vec<Value>),
46
47    /// String value, either borrowed or owned
48    String(String),
49
50    /// An array of bytes that is more efficient than List(u8)
51    Bytes(Vec<u8>),
52
53    /// 128-bit universally unique identifier (UUID)
54    Uuid(uuid::Uuid),
55
56    /// A fixed-precision decimal number.
57    /// See [`rust_decimal::Decimal`].
58    #[cfg(feature = "rust_decimal")]
59    Decimal(rust_decimal::Decimal),
60
61    /// An arbitrary-precision decimal number.
62    /// See [`bigdecimal::BigDecimal`].
63    #[cfg(feature = "bigdecimal")]
64    BigDecimal(bigdecimal::BigDecimal),
65
66    /// An instant in time represented as the number of nanoseconds since the Unix epoch.
67    /// See [`jiff::Timestamp`].
68    #[cfg(feature = "jiff")]
69    Timestamp(jiff::Timestamp),
70
71    /// A time zone aware instant in time.
72    /// See [`jiff::Zoned`]
73    #[cfg(feature = "jiff")]
74    Zoned(jiff::Zoned),
75
76    /// A representation of a civil date in the Gregorian calendar.
77    /// See [`jiff::civil::Date`].
78    #[cfg(feature = "jiff")]
79    Date(jiff::civil::Date),
80
81    /// A representation of civil “wall clock” time.
82    /// See [`jiff::civil::Time`].
83    #[cfg(feature = "jiff")]
84    Time(jiff::civil::Time),
85
86    /// A representation of a civil datetime in the Gregorian calendar.
87    /// See [`jiff::civil::DateTime`].
88    #[cfg(feature = "jiff")]
89    DateTime(jiff::civil::DateTime),
90}
91
92impl Value {
93    /// Returns a `ValueCow` representing null
94    pub const fn null() -> Self {
95        Self::Null
96    }
97
98    pub const fn is_null(&self) -> bool {
99        matches!(self, Self::Null)
100    }
101
102    pub const fn is_record(&self) -> bool {
103        matches!(self, Self::Record(_))
104    }
105
106    pub fn record_from_vec(fields: Vec<Self>) -> Self {
107        ValueRecord::from_vec(fields).into()
108    }
109
110    /// Create a `ValueCow` representing the given boolean value
111    pub const fn from_bool(src: bool) -> Self {
112        Self::Bool(src)
113    }
114
115    pub fn as_str(&self) -> Option<&str> {
116        match self {
117            Self::String(v) => Some(&**v),
118            _ => None,
119        }
120    }
121
122    pub fn expect_string(&self) -> &str {
123        match self {
124            Self::String(v) => v,
125            _ => todo!(),
126        }
127    }
128
129    pub fn as_record(&self) -> Option<&ValueRecord> {
130        match self {
131            Self::Record(record) => Some(record),
132            _ => None,
133        }
134    }
135
136    pub fn expect_record(&self) -> &ValueRecord {
137        match self {
138            Self::Record(record) => record,
139            _ => panic!("{self:#?}"),
140        }
141    }
142
143    pub fn expect_record_mut(&mut self) -> &mut ValueRecord {
144        match self {
145            Self::Record(record) => record,
146            _ => panic!(),
147        }
148    }
149
150    pub fn into_record(self) -> ValueRecord {
151        match self {
152            Self::Record(record) => record,
153            _ => panic!(),
154        }
155    }
156
157    pub fn is_a(&self, ty: &Type) -> bool {
158        if let Type::Union(types) = ty {
159            return types.iter().any(|t| self.is_a(t));
160        }
161        match self {
162            Self::Null => true,
163            Self::Bool(_) => ty.is_bool(),
164            Self::I8(_) => ty.is_i8(),
165            Self::I16(_) => ty.is_i16(),
166            Self::I32(_) => ty.is_i32(),
167            Self::I64(_) => ty.is_i64(),
168            Self::U8(_) => ty.is_u8(),
169            Self::U16(_) => ty.is_u16(),
170            Self::U32(_) => ty.is_u32(),
171            Self::U64(_) => ty.is_u64(),
172            Self::List(value) => match ty {
173                Type::List(ty) => {
174                    if value.is_empty() {
175                        true
176                    } else {
177                        value[0].is_a(ty)
178                    }
179                }
180                _ => false,
181            },
182            Self::Record(value) => match ty {
183                Type::Record(fields) if value.len() == fields.len() => value
184                    .fields
185                    .iter()
186                    .zip(fields.iter())
187                    .all(|(value, ty)| value.is_a(ty)),
188                _ => false,
189            },
190            Self::SparseRecord(value) => match ty {
191                Type::SparseRecord(fields) => value.fields == *fields,
192                _ => false,
193            },
194            Self::String(_) => ty.is_string(),
195            Self::Bytes(_) => ty.is_bytes(),
196            Self::Uuid(_) => ty.is_uuid(),
197            #[cfg(feature = "rust_decimal")]
198            Value::Decimal(_) => *ty == Type::Decimal,
199            #[cfg(feature = "bigdecimal")]
200            Value::BigDecimal(_) => *ty == Type::BigDecimal,
201            #[cfg(feature = "jiff")]
202            Value::Timestamp(_) => *ty == Type::Timestamp,
203            #[cfg(feature = "jiff")]
204            Value::Zoned(_) => *ty == Type::Zoned,
205            #[cfg(feature = "jiff")]
206            Value::Date(_) => *ty == Type::Date,
207            #[cfg(feature = "jiff")]
208            Value::Time(_) => *ty == Type::Time,
209            #[cfg(feature = "jiff")]
210            Value::DateTime(_) => *ty == Type::DateTime,
211        }
212    }
213
214    /// Infer the type of a value
215    pub fn infer_ty(&self) -> Type {
216        match self {
217            Value::Bool(_) => Type::Bool,
218            Value::I8(_) => Type::I8,
219            Value::I16(_) => Type::I16,
220            Value::I32(_) => Type::I32,
221            Value::I64(_) => Type::I64,
222            Value::SparseRecord(v) => Type::SparseRecord(v.fields.clone()),
223            Value::Null => Type::Null,
224            Value::Record(v) => Type::Record(v.fields.iter().map(Self::infer_ty).collect()),
225            Value::String(_) => Type::String,
226            Value::List(items) if items.is_empty() => Type::list(Type::Null),
227            Value::List(items) => {
228                let mut union = TypeUnion::new();
229                for item in items {
230                    union.insert(item.infer_ty());
231                }
232                Type::list(union.simplify())
233            }
234            Value::U8(_) => Type::U8,
235            Value::U16(_) => Type::U16,
236            Value::U32(_) => Type::U32,
237            Value::U64(_) => Type::U64,
238            Value::Bytes(_) => Type::Bytes,
239            Value::Uuid(_) => Type::Uuid,
240            #[cfg(feature = "rust_decimal")]
241            Value::Decimal(_) => Type::Decimal,
242            #[cfg(feature = "bigdecimal")]
243            Value::BigDecimal(_) => Type::BigDecimal,
244            #[cfg(feature = "jiff")]
245            Value::Timestamp(_) => Type::Timestamp,
246            #[cfg(feature = "jiff")]
247            Value::Zoned(_) => Type::Zoned,
248            #[cfg(feature = "jiff")]
249            Value::Date(_) => Type::Date,
250            #[cfg(feature = "jiff")]
251            Value::Time(_) => Type::Time,
252            #[cfg(feature = "jiff")]
253            Value::DateTime(_) => Type::DateTime,
254        }
255    }
256
257    #[track_caller]
258    pub fn entry(&self, path: impl EntryPath) -> Entry<'_> {
259        let mut ret = Entry::Value(self);
260
261        for step in path.step_iter() {
262            ret = match ret {
263                Entry::Value(Self::Record(record)) => Entry::Value(&record[step]),
264                Entry::Value(Self::List(items)) => Entry::Value(&items[step]),
265                _ => todo!("ret={ret:#?}; base={self:#?}; step={step:#?}"),
266            }
267        }
268
269        ret
270    }
271
272    pub fn take(&mut self) -> Self {
273        std::mem::take(self)
274    }
275}
276
277impl AsRef<Self> for Value {
278    fn as_ref(&self) -> &Self {
279        self
280    }
281}
282
283impl PartialOrd for Value {
284    /// Compares two values if they are of the same type.
285    ///
286    /// Returns `None` for:
287    ///
288    /// - `null` values (SQL semantics, e.g., `null` comparisons are undefined)
289    /// - Comparisons across different types
290    /// - Types without natural ordering (records, lists, etc.)
291    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
292        match (self, other) {
293            // `null` comparisons are undefined.
294            (Value::Null, _) | (_, Value::Null) => None,
295
296            // Booleans.
297            (Value::Bool(a), Value::Bool(b)) => a.partial_cmp(b),
298
299            // Signed integers.
300            (Value::I8(a), Value::I8(b)) => a.partial_cmp(b),
301            (Value::I16(a), Value::I16(b)) => a.partial_cmp(b),
302            (Value::I32(a), Value::I32(b)) => a.partial_cmp(b),
303            (Value::I64(a), Value::I64(b)) => a.partial_cmp(b),
304
305            // Unsigned integers.
306            (Value::U8(a), Value::U8(b)) => a.partial_cmp(b),
307            (Value::U16(a), Value::U16(b)) => a.partial_cmp(b),
308            (Value::U32(a), Value::U32(b)) => a.partial_cmp(b),
309            (Value::U64(a), Value::U64(b)) => a.partial_cmp(b),
310
311            // Strings: lexicographic ordering.
312            (Value::String(a), Value::String(b)) => a.partial_cmp(b),
313
314            // Bytes: lexicographic ordering.
315            (Value::Bytes(a), Value::Bytes(b)) => a.partial_cmp(b),
316
317            // UUIDs.
318            (Value::Uuid(a), Value::Uuid(b)) => a.partial_cmp(b),
319
320            // Decimal: fixed-precision decimal numbers.
321            #[cfg(feature = "rust_decimal")]
322            (Value::Decimal(a), Value::Decimal(b)) => a.partial_cmp(b),
323
324            // BigDecimal: arbitrary-precision decimal numbers.
325            #[cfg(feature = "bigdecimal")]
326            (Value::BigDecimal(a), Value::BigDecimal(b)) => a.partial_cmp(b),
327
328            // Date/time types.
329            #[cfg(feature = "jiff")]
330            (Value::Timestamp(a), Value::Timestamp(b)) => a.partial_cmp(b),
331            #[cfg(feature = "jiff")]
332            (Value::Zoned(a), Value::Zoned(b)) => a.partial_cmp(b),
333            #[cfg(feature = "jiff")]
334            (Value::Date(a), Value::Date(b)) => a.partial_cmp(b),
335            #[cfg(feature = "jiff")]
336            (Value::Time(a), Value::Time(b)) => a.partial_cmp(b),
337            #[cfg(feature = "jiff")]
338            (Value::DateTime(a), Value::DateTime(b)) => a.partial_cmp(b),
339
340            // Types without natural ordering or different types.
341            _ => None,
342        }
343    }
344}
345
346impl From<bool> for Value {
347    fn from(src: bool) -> Self {
348        Self::Bool(src)
349    }
350}
351
352impl TryFrom<Value> for bool {
353    type Error = crate::Error;
354
355    fn try_from(value: Value) -> Result<Self, Self::Error> {
356        match value {
357            Value::Bool(v) => Ok(v),
358            _ => Err(crate::Error::type_conversion(value, "bool")),
359        }
360    }
361}
362
363impl From<String> for Value {
364    fn from(src: String) -> Self {
365        Self::String(src)
366    }
367}
368
369impl From<&String> for Value {
370    fn from(src: &String) -> Self {
371        Self::String(src.clone())
372    }
373}
374
375impl From<&str> for Value {
376    fn from(src: &str) -> Self {
377        Self::String(src.to_string())
378    }
379}
380
381impl From<ValueRecord> for Value {
382    fn from(value: ValueRecord) -> Self {
383        Self::Record(value)
384    }
385}
386
387impl<T> From<Option<T>> for Value
388where
389    Self: From<T>,
390{
391    fn from(value: Option<T>) -> Self {
392        match value {
393            Some(value) => Self::from(value),
394            None => Self::Null,
395        }
396    }
397}
398
399impl TryFrom<Value> for String {
400    type Error = crate::Error;
401
402    fn try_from(value: Value) -> Result<Self, Self::Error> {
403        match value {
404            Value::String(v) => Ok(v),
405            _ => Err(crate::Error::type_conversion(value, "String")),
406        }
407    }
408}
409
410impl From<Vec<u8>> for Value {
411    fn from(value: Vec<u8>) -> Self {
412        Self::Bytes(value)
413    }
414}
415
416impl TryFrom<Value> for Vec<u8> {
417    type Error = crate::Error;
418
419    fn try_from(value: Value) -> Result<Self, Self::Error> {
420        match value {
421            Value::Bytes(v) => Ok(v),
422            _ => Err(crate::Error::type_conversion(value, "Bytes")),
423        }
424    }
425}
426
427impl From<uuid::Uuid> for Value {
428    fn from(value: uuid::Uuid) -> Self {
429        Self::Uuid(value)
430    }
431}
432
433impl TryFrom<Value> for uuid::Uuid {
434    type Error = crate::Error;
435
436    fn try_from(value: Value) -> Result<Self, Self::Error> {
437        match value {
438            Value::Uuid(v) => Ok(v),
439            _ => Err(crate::Error::type_conversion(value, "uuid::Uuid")),
440        }
441    }
442}
443
444#[cfg(feature = "rust_decimal")]
445impl From<rust_decimal::Decimal> for Value {
446    fn from(value: rust_decimal::Decimal) -> Self {
447        Self::Decimal(value)
448    }
449}
450
451#[cfg(feature = "rust_decimal")]
452impl TryFrom<Value> for rust_decimal::Decimal {
453    type Error = crate::Error;
454
455    fn try_from(value: Value) -> Result<Self, Self::Error> {
456        match value {
457            Value::Decimal(v) => Ok(v),
458            _ => Err(crate::Error::type_conversion(
459                value,
460                "rust_decimal::Decimal",
461            )),
462        }
463    }
464}
465
466#[cfg(feature = "bigdecimal")]
467impl From<bigdecimal::BigDecimal> for Value {
468    fn from(value: bigdecimal::BigDecimal) -> Self {
469        Self::BigDecimal(value)
470    }
471}
472
473#[cfg(feature = "bigdecimal")]
474impl TryFrom<Value> for bigdecimal::BigDecimal {
475    type Error = crate::Error;
476
477    fn try_from(value: Value) -> Result<Self, Self::Error> {
478        match value {
479            Value::BigDecimal(v) => Ok(v),
480            _ => Err(crate::Error::type_conversion(
481                value,
482                "bigdecimal::BigDecimal",
483            )),
484        }
485    }
486}