Skip to main content

qraft_core/
ty.rs

1//! Type metadata and small type-level computations used by the expression system.
2
3use std::{
4    borrow::Cow,
5    marker::PhantomData,
6    num::{ParseFloatError, ParseIntError, TryFromIntError},
7    string::FromUtf8Error,
8};
9
10use quex::{Date as QuexDate, DateTime as QuexDateTime, Error as QuexError, Time as QuexTime};
11
12use crate::{
13    DbValue, DefaultMeta,
14    span::{Span, TextSpan},
15};
16
17fn cast_message(kind: crate::ValueKind, to: &'static str, reason: impl std::fmt::Display) -> QuexError {
18    QuexError::Unsupported(format!("cannot cast {} to {}: {}", kind, to, reason))
19}
20
21fn mismatch(kind: crate::ValueKind, to: &'static str) -> QuexError {
22    cast_message(kind, to, "type mismatch")
23}
24
25fn overflow(kind: crate::ValueKind, to: &'static str) -> QuexError {
26    cast_message(kind, to, "overflow during conversion")
27}
28
29fn utf8(kind: crate::ValueKind, to: &'static str, error: FromUtf8Error) -> QuexError {
30    cast_message(kind, to, error)
31}
32
33fn parse_int(kind: crate::ValueKind, to: &'static str, error: ParseIntError) -> QuexError {
34    cast_message(kind, to, error)
35}
36
37fn parse_float(kind: crate::ValueKind, to: &'static str, error: ParseFloatError) -> QuexError {
38    cast_message(kind, to, error)
39}
40
41fn try_from_int(kind: crate::ValueKind, to: &'static str, error: TryFromIntError) -> QuexError {
42    cast_message(kind, to, error)
43}
44
45/// Describes the borrowed runtime representation for a SQL type.
46pub trait TypeMeta {
47    /// Borrowed or owned representation used while binding or decoding values.
48    type Repr<'a>;
49}
50
51/// Marker for nullable SQL expressions in type-level computations.
52pub struct Null;
53/// Marker for non-null SQL expressions in type-level computations.
54pub struct NotNull {
55    _priv: (),
56}
57
58/// Exposes whether a SQL type is nullable.
59pub trait Nullability: TypeMeta {
60    /// Nullability marker for the type.
61    type Null;
62}
63
64/// Connects a SQL type to its Rust value type and decoding rules.
65pub trait TypeCast: TypeMeta + Sized {
66    /// Rust type commonly used for this SQL type.
67    type From: DefaultMeta<Meta = Self>;
68    /// Stored parameter representation after interning.
69    type Inner;
70    /// Borrows a Rust value as this SQL type's runtime representation.
71    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a>;
72    /// Decodes a driver-neutral value into this SQL type's representation.
73    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>>;
74}
75
76/// Adapts one `TypeCast` source into another borrowed view when that is useful.
77pub trait AsCast<T: TypeCast> {
78    type Cast<'a>
79    where
80        T: 'a;
81    /// Borrows a `T::From` value through an alternate cast view.
82    fn as_cast<'a>(value: &'a T::From) -> Self::Cast<'a>;
83}
84
85/// Marks types that may appear in required columns.
86pub trait Required: TypeMeta {}
87/// Marks types that support equality predicates.
88pub trait Comparable: TypeMeta {}
89/// Marks types that support ordering predicates.
90pub trait Orderable: TypeMeta {}
91/// Marks types that support numeric operators.
92pub trait Numeric: TypeMeta {}
93/// Marks types that can participate in logical operations.
94pub trait Logical: TypeMeta {}
95/// Marks boolean-like logical types.
96pub trait Boolean: Logical {}
97/// Marks types that can be used with pattern matching predicates.
98pub trait Likeable: TypeMeta {}
99
100/// Placeholder type for untyped expressions such as raw column names.
101pub struct Untyped;
102impl TypeMeta for Untyped {
103    type Repr<'a> = ();
104}
105impl Nullability for Untyped {
106    type Null = NotNull;
107}
108impl Comparable for Untyped {}
109impl Orderable for Untyped {}
110impl Numeric for Untyped {}
111impl Logical for Untyped {}
112impl Likeable for Untyped {}
113impl Required for Untyped {}
114
115/// SQL boolean type.
116pub struct Bool;
117impl TypeMeta for Bool {
118    type Repr<'a> = bool;
119}
120impl Nullability for Bool {
121    type Null = NotNull;
122}
123impl Required for Bool {}
124impl Comparable for Bool {}
125impl Logical for Bool {}
126impl Boolean for Bool {}
127
128impl TypeCast for Bool {
129    type From = bool;
130
131    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
132        *value
133    }
134
135    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
136        match value {
137            DbValue::Bool(v) => Ok(v),
138            DbValue::Unsigned(v) if v == 0 || v == 1 => Ok(v == 1),
139            DbValue::BigInt(v) if v == 0 || v == 1 => Ok(v == 1),
140            DbValue::Double(v) if v == 0.0 || v == 1.0 => Ok(v == 1.0),
141            DbValue::Text(v) if v == "false" || v == "true" => Ok(v == "true"),
142            value => Err(mismatch(value.kind(), std::any::type_name::<bool>())),
143        }
144    }
145
146    type Inner = bool;
147}
148
149/// SQL 32-bit signed integer type.
150pub struct Int;
151impl TypeMeta for Int {
152    type Repr<'a> = i32;
153}
154impl Nullability for Int {
155    type Null = NotNull;
156}
157impl Required for Int {}
158impl Comparable for Int {}
159impl Orderable for Int {}
160impl Numeric for Int {}
161
162impl TypeCast for Int {
163    type From = i32;
164
165    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
166        *value
167    }
168
169    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
170        let kind = value.kind();
171        let to = std::any::type_name::<i32>();
172
173        match value {
174            DbValue::Bool(v) => Ok(v.into()),
175            DbValue::Unsigned(v) => v
176                .try_into()
177                .map_err(|e| try_from_int(kind, to, e)),
178            DbValue::BigInt(v) => v
179                .try_into()
180                .map_err(|e| try_from_int(kind, to, e)),
181            DbValue::Text(v) => v
182                .parse::<i32>()
183                .map_err(|e| parse_int(kind, to, e)),
184            _ => Err(mismatch(kind, to)),
185        }
186    }
187
188    type Inner = i32;
189}
190
191/// SQL 64-bit signed integer type.
192pub struct BigInt;
193impl TypeMeta for BigInt {
194    type Repr<'a> = i64;
195}
196impl Nullability for BigInt {
197    type Null = NotNull;
198}
199impl Required for BigInt {}
200impl Comparable for BigInt {}
201impl Orderable for BigInt {}
202impl Numeric for BigInt {}
203
204impl TypeCast for BigInt {
205    type From = i64;
206
207    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
208        *value
209    }
210
211    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
212        let kind = value.kind();
213        let to = std::any::type_name::<i64>();
214
215        match value {
216            DbValue::Bool(v) => Ok(v.into()),
217            DbValue::Unsigned(v) => v
218                .try_into()
219                .map_err(|e| try_from_int(kind, to, e)),
220            DbValue::BigInt(v) => Ok(v),
221            DbValue::Text(v) => v
222                .parse::<i64>()
223                .map_err(|e| parse_int(kind, to, e)),
224            _ => Err(mismatch(kind, to)),
225        }
226    }
227
228    type Inner = i64;
229}
230
231/// SQL 64-bit unsigned integer type.
232pub struct UBigInt;
233impl TypeMeta for UBigInt {
234    type Repr<'a> = u64;
235}
236impl Nullability for UBigInt {
237    type Null = NotNull;
238}
239impl Required for UBigInt {}
240impl Comparable for UBigInt {}
241impl Orderable for UBigInt {}
242impl Numeric for UBigInt {}
243
244impl TypeCast for UBigInt {
245    type From = u64;
246
247    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
248        *value
249    }
250
251    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
252        let kind = value.kind();
253        let to = std::any::type_name::<u64>();
254
255        match value {
256            DbValue::Bool(v) => Ok(v.into()),
257            DbValue::Unsigned(v) => Ok(v),
258            DbValue::BigInt(v) => v
259                .try_into()
260                .map_err(|e| try_from_int(kind, to, e)),
261            DbValue::Text(v) => v
262                .parse::<u64>()
263                .map_err(|e| parse_int(kind, to, e)),
264            _ => Err(mismatch(kind, to)),
265        }
266    }
267
268    type Inner = u64;
269}
270
271/// SQL 32-bit unsigned integer type.
272pub struct UInt;
273impl TypeMeta for UInt {
274    type Repr<'a> = u32;
275}
276impl Nullability for UInt {
277    type Null = NotNull;
278}
279impl Required for UInt {}
280impl Comparable for UInt {}
281impl Orderable for UInt {}
282impl Numeric for UInt {}
283
284impl TypeCast for UInt {
285    type From = u32;
286
287    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
288        *value
289    }
290
291    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
292        let kind = value.kind();
293        let to = std::any::type_name::<u32>();
294
295        match value {
296            DbValue::Bool(v) => Ok(v.into()),
297            DbValue::Unsigned(v) => v
298                .try_into()
299                .map_err(|e| try_from_int(kind, to, e)),
300            DbValue::BigInt(v) => v
301                .try_into()
302                .map_err(|e| try_from_int(kind, to, e)),
303            DbValue::Text(v) => v
304                .parse::<u32>()
305                .map_err(|e| parse_int(kind, to, e)),
306            _ => Err(mismatch(kind, to)),
307        }
308    }
309
310    type Inner = u32;
311}
312
313/// SQL single-precision floating-point type.
314pub struct Float;
315impl TypeMeta for Float {
316    type Repr<'a> = f32;
317}
318impl Nullability for Float {
319    type Null = NotNull;
320}
321impl Required for Float {}
322impl Comparable for Float {}
323impl Orderable for Float {}
324impl Numeric for Float {}
325
326impl TypeCast for Float {
327    type From = f32;
328
329    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
330        *value
331    }
332
333    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
334        let kind = value.kind();
335        let to = std::any::type_name::<f32>();
336
337        match value {
338            DbValue::Bool(v) => Ok((v as u8).into()),
339            DbValue::Unsigned(v) => {
340                let value: u16 = v
341                    .try_into()
342                    .map_err(|e| try_from_int(kind, to, e))?;
343                Ok(value.into())
344            }
345            DbValue::BigInt(v) => {
346                let value: i16 = v
347                    .try_into()
348                    .map_err(|e| try_from_int(kind, to, e))?;
349                Ok(value.into())
350            }
351            DbValue::Double(v) => {
352                if !v.is_finite() || v > f32::MAX as f64 || v < f32::MIN as f64 {
353                    return Err(overflow(kind, to));
354                }
355                Ok(v as f32)
356            }
357            DbValue::Text(v) => v
358                .parse::<f32>()
359                .map_err(|e| parse_float(kind, to, e)),
360            _ => Err(mismatch(kind, to)),
361        }
362    }
363
364    type Inner = f32;
365}
366
367/// SQL double-precision floating-point type.
368pub struct Double;
369impl TypeMeta for Double {
370    type Repr<'a> = f64;
371}
372impl Nullability for Double {
373    type Null = NotNull;
374}
375impl Required for Double {}
376impl Comparable for Double {}
377impl Orderable for Double {}
378impl Numeric for Double {}
379
380impl TypeCast for Double {
381    type From = f64;
382
383    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
384        *value
385    }
386
387    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
388        let kind = value.kind();
389        let to = std::any::type_name::<f64>();
390
391        match value {
392            DbValue::Bool(v) => Ok((v as u8).into()),
393            DbValue::Unsigned(v) => {
394                let value: u32 = v
395                    .try_into()
396                    .map_err(|e| try_from_int(kind, to, e))?;
397                Ok(value.into())
398            }
399            DbValue::BigInt(v) => {
400                let value: i32 = v
401                    .try_into()
402                    .map_err(|e| try_from_int(kind, to, e))?;
403                Ok(value.into())
404            }
405            DbValue::Double(v) => Ok(v),
406            DbValue::Text(v) => v
407                .parse::<f64>()
408                .map_err(|e| parse_float(kind, to, e)),
409            _ => Err(mismatch(kind, to)),
410        }
411    }
412
413    type Inner = f64;
414}
415
416/// SQL text type.
417pub struct Text;
418impl TypeMeta for Text {
419    type Repr<'a> = Cow<'a, str>;
420}
421impl Nullability for Text {
422    type Null = NotNull;
423}
424impl Required for Text {}
425impl Comparable for Text {}
426impl Orderable for Text {}
427impl Likeable for Text {}
428
429impl TypeCast for Text {
430    type From = String;
431
432    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
433        Cow::Borrowed(value.as_str())
434    }
435
436    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
437        let kind = value.kind();
438        let to = std::any::type_name::<String>();
439
440        match value {
441            DbValue::Text(v) => Ok(Cow::Owned(v)),
442            DbValue::Blob(v) => String::from_utf8(v)
443                .map(Cow::Owned)
444                .map_err(|e| utf8(kind, to, e)),
445            _ => Err(mismatch(kind, to)),
446        }
447    }
448
449    type Inner = TextSpan;
450}
451
452impl AsCast<Text> for Text {
453    type Cast<'a> = Cow<'a, str>;
454    fn as_cast<'a>(value: &'a String) -> Self::Cast<'a> {
455        Cow::Borrowed(value.as_str())
456    }
457}
458
459impl AsCast<Blob> for Text {
460    type Cast<'a> = Cow<'a, str>;
461    fn as_cast<'a>(value: &'a Vec<u8>) -> Self::Cast<'a> {
462        Cow::Owned(std::str::from_utf8(value).unwrap().to_owned())
463    }
464}
465
466/// SQL date type.
467pub struct Date;
468impl TypeMeta for Date {
469    type Repr<'a> = Cow<'a, str>;
470}
471impl Nullability for Date {
472    type Null = NotNull;
473}
474impl Required for Date {}
475impl Comparable for Date {}
476impl Orderable for Date {}
477
478impl TypeCast for Date {
479    type From = QuexDate;
480
481    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
482        Cow::Owned(format!(
483            "{:04}-{:02}-{:02}",
484            value.year, value.month, value.day
485        ))
486    }
487
488    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
489        Text::from_db_value(value)
490    }
491
492    type Inner = TextSpan;
493}
494
495/// SQL time type.
496pub struct Time;
497impl TypeMeta for Time {
498    type Repr<'a> = Cow<'a, str>;
499}
500impl Nullability for Time {
501    type Null = NotNull;
502}
503impl Required for Time {}
504impl Comparable for Time {}
505impl Orderable for Time {}
506
507impl TypeCast for Time {
508    type From = QuexTime;
509
510    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
511        format_time(*value)
512    }
513
514    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
515        Text::from_db_value(value)
516    }
517
518    type Inner = TextSpan;
519}
520
521/// SQL timestamp type.
522pub struct Timestamp;
523impl TypeMeta for Timestamp {
524    type Repr<'a> = Cow<'a, str>;
525}
526impl Nullability for Timestamp {
527    type Null = NotNull;
528}
529impl Required for Timestamp {}
530impl Comparable for Timestamp {}
531impl Orderable for Timestamp {}
532
533impl TypeCast for Timestamp {
534    type From = QuexDateTime;
535
536    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
537        Cow::Owned(format!(
538            "{:04}-{:02}-{:02} {}",
539            value.date.year,
540            value.date.month,
541            value.date.day,
542            format_time(value.time)
543        ))
544    }
545
546    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
547        Text::from_db_value(value)
548    }
549
550    type Inner = TextSpan;
551}
552
553fn format_time(value: QuexTime) -> Cow<'static, str> {
554    if value.microsecond == 0 {
555        Cow::Owned(format!(
556            "{:02}:{:02}:{:02}",
557            value.hour, value.minute, value.second
558        ))
559    } else {
560        let mut micros = format!("{:06}", value.microsecond);
561        while micros.ends_with('0') {
562            micros.pop();
563        }
564
565        Cow::Owned(format!(
566            "{:02}:{:02}:{:02}.{}",
567            value.hour, value.minute, value.second, micros
568        ))
569    }
570}
571
572/// SQL binary/blob type.
573pub struct Blob;
574impl TypeMeta for Blob {
575    type Repr<'a> = Cow<'a, [u8]>;
576}
577impl Nullability for Blob {
578    type Null = NotNull;
579}
580impl Required for Blob {}
581impl Comparable for Blob {}
582impl Orderable for Blob {}
583
584impl TypeCast for Blob {
585    type From = Vec<u8>;
586
587    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
588        Cow::Borrowed(value.as_ref())
589    }
590
591    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
592        let kind = value.kind();
593        let to = std::any::type_name::<Vec<u8>>();
594
595        match value {
596            DbValue::Text(v) => Ok(Cow::Owned(v.into_bytes())),
597            DbValue::Blob(v) => Ok(Cow::Owned(v)),
598            _ => Err(mismatch(kind, to)),
599        }
600    }
601
602    type Inner = Span;
603}
604
605/// Nullable wrapper for another SQL type.
606pub struct Nullable<T> {
607    marker: PhantomData<T>,
608}
609
610impl<T: Comparable + Required> Comparable for Nullable<T> {}
611impl<T: Orderable + Required> Orderable for Nullable<T> {}
612impl<T: Numeric + Required> Numeric for Nullable<T> {}
613impl<T: Logical + Required> Logical for Nullable<T> {}
614impl<T: Boolean + Required> Boolean for Nullable<T> {}
615impl<T: Likeable + Required> Likeable for Nullable<T> {}
616
617impl<T> TypeCast for Nullable<T>
618where
619    T: TypeCast,
620{
621    type From = Option<T::From>;
622
623    fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
624        value.as_ref().map(|v| T::as_cast(v))
625    }
626
627    fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
628        match value {
629            DbValue::Null => Ok(None),
630            value => T::from_db_value(value).map(Some),
631        }
632    }
633
634    type Inner = Option<T::Inner>;
635}
636
637impl<T> TypeMeta for Nullable<T>
638where
639    T: TypeMeta,
640{
641    type Repr<'a> = Option<T::Repr<'a>>;
642}
643
644impl<T> Nullability for Nullable<T>
645where
646    T: TypeMeta,
647{
648    type Null = Null;
649}
650
651/// Convenience alias for the nullability marker of a type.
652pub type NullOf<T> = <T as Nullability>::Null;
653
654/// Computes the boolean output type of a predicate based on nullability.
655pub trait PredicateType {
656    /// Resulting boolean type.
657    type Output: Boolean;
658}
659
660/// Type-level predicate computation for one operand.
661pub struct UnaryType<L> {
662    marker: PhantomData<L>,
663}
664
665impl PredicateType for UnaryType<NotNull> {
666    type Output = Bool;
667}
668
669impl PredicateType for UnaryType<Null> {
670    type Output = Nullable<Bool>;
671}
672
673/// Type-level predicate or numeric computation for two operands.
674pub struct BinaryType<L, R> {
675    marker: PhantomData<(L, R)>,
676}
677
678impl PredicateType for BinaryType<NotNull, NotNull> {
679    type Output = Bool;
680}
681
682impl PredicateType for BinaryType<NotNull, Null> {
683    type Output = Nullable<Bool>;
684}
685
686impl PredicateType for BinaryType<Null, NotNull> {
687    type Output = Nullable<Bool>;
688}
689
690impl PredicateType for BinaryType<Null, Null> {
691    type Output = Nullable<Bool>;
692}
693
694/// Type-level predicate computation for three operands.
695pub struct TernaryType<P, Q, R> {
696    marker: PhantomData<(P, Q, R)>,
697}
698
699impl PredicateType for TernaryType<NotNull, NotNull, NotNull> {
700    type Output = Bool;
701}
702
703impl PredicateType for TernaryType<Null, NotNull, NotNull> {
704    type Output = Nullable<Bool>;
705}
706
707impl PredicateType for TernaryType<NotNull, Null, NotNull> {
708    type Output = Nullable<Bool>;
709}
710
711impl PredicateType for TernaryType<NotNull, NotNull, Null> {
712    type Output = Nullable<Bool>;
713}
714
715impl PredicateType for TernaryType<Null, Null, NotNull> {
716    type Output = Nullable<Bool>;
717}
718
719impl PredicateType for TernaryType<Null, NotNull, Null> {
720    type Output = Nullable<Bool>;
721}
722
723impl PredicateType for TernaryType<NotNull, Null, Null> {
724    type Output = Nullable<Bool>;
725}
726
727impl PredicateType for TernaryType<Null, Null, Null> {
728    type Output = Nullable<Bool>;
729}
730
731/// Computes the numeric output type for binary math expressions.
732pub trait MathType {
733    /// Resulting numeric type.
734    type Output: Numeric;
735}
736
737macro_rules! binary_numeric {
738    ($left:ty, $right:ty => $out:ty) => {
739        impl MathType for BinaryType<$left, $right> {
740            type Output = $out;
741        }
742
743        impl MathType for BinaryType<$right, $left> {
744            type Output = $out;
745        }
746    };
747}
748
749impl MathType for BinaryType<Int, Int> {
750    type Output = Int;
751}
752
753impl MathType for BinaryType<BigInt, BigInt> {
754    type Output = BigInt;
755}
756
757impl MathType for BinaryType<UInt, UInt> {
758    type Output = UInt;
759}
760
761impl MathType for BinaryType<UBigInt, UBigInt> {
762    type Output = UBigInt;
763}
764
765impl MathType for BinaryType<Float, Float> {
766    type Output = Float;
767}
768
769impl MathType for BinaryType<Double, Double> {
770    type Output = Double;
771}
772
773binary_numeric!(Int, BigInt => BigInt);
774binary_numeric!(Int, Float => Float);
775binary_numeric!(Int, Double => Double);
776binary_numeric!(BigInt, Float => Float);
777binary_numeric!(BigInt, Double => Double);
778binary_numeric!(UInt, UBigInt => UBigInt);
779binary_numeric!(UInt, Float => Float);
780binary_numeric!(UInt, Double => Double);
781binary_numeric!(UBigInt, Float => Float);
782binary_numeric!(UBigInt, Double => Double);
783binary_numeric!(Float, Double => Double);