use std::{
borrow::Cow,
marker::PhantomData,
num::{ParseFloatError, ParseIntError, TryFromIntError},
string::FromUtf8Error,
};
use quex::{Date as QuexDate, DateTime as QuexDateTime, Error as QuexError, Time as QuexTime};
use crate::{
DbValue, DefaultMeta,
span::{Span, TextSpan},
};
fn cast_message(kind: crate::ValueKind, to: &'static str, reason: impl std::fmt::Display) -> QuexError {
QuexError::Unsupported(format!("cannot cast {} to {}: {}", kind, to, reason))
}
fn mismatch(kind: crate::ValueKind, to: &'static str) -> QuexError {
cast_message(kind, to, "type mismatch")
}
fn overflow(kind: crate::ValueKind, to: &'static str) -> QuexError {
cast_message(kind, to, "overflow during conversion")
}
fn utf8(kind: crate::ValueKind, to: &'static str, error: FromUtf8Error) -> QuexError {
cast_message(kind, to, error)
}
fn parse_int(kind: crate::ValueKind, to: &'static str, error: ParseIntError) -> QuexError {
cast_message(kind, to, error)
}
fn parse_float(kind: crate::ValueKind, to: &'static str, error: ParseFloatError) -> QuexError {
cast_message(kind, to, error)
}
fn try_from_int(kind: crate::ValueKind, to: &'static str, error: TryFromIntError) -> QuexError {
cast_message(kind, to, error)
}
pub trait TypeMeta {
type Repr<'a>;
}
pub struct Null;
pub struct NotNull {
_priv: (),
}
pub trait Nullability: TypeMeta {
type Null;
}
pub trait TypeCast: TypeMeta + Sized {
type From: DefaultMeta<Meta = Self>;
type Inner;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a>;
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>>;
}
pub trait AsCast<T: TypeCast> {
type Cast<'a>
where
T: 'a;
fn as_cast<'a>(value: &'a T::From) -> Self::Cast<'a>;
}
pub trait Required: TypeMeta {}
pub trait Comparable: TypeMeta {}
pub trait Orderable: TypeMeta {}
pub trait Numeric: TypeMeta {}
pub trait Logical: TypeMeta {}
pub trait Boolean: Logical {}
pub trait Likeable: TypeMeta {}
pub struct Untyped;
impl TypeMeta for Untyped {
type Repr<'a> = ();
}
impl Nullability for Untyped {
type Null = NotNull;
}
impl Comparable for Untyped {}
impl Orderable for Untyped {}
impl Numeric for Untyped {}
impl Logical for Untyped {}
impl Likeable for Untyped {}
impl Required for Untyped {}
pub struct Bool;
impl TypeMeta for Bool {
type Repr<'a> = bool;
}
impl Nullability for Bool {
type Null = NotNull;
}
impl Required for Bool {}
impl Comparable for Bool {}
impl Logical for Bool {}
impl Boolean for Bool {}
impl TypeCast for Bool {
type From = bool;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
*value
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
match value {
DbValue::Bool(v) => Ok(v),
DbValue::Unsigned(v) if v == 0 || v == 1 => Ok(v == 1),
DbValue::BigInt(v) if v == 0 || v == 1 => Ok(v == 1),
DbValue::Double(v) if v == 0.0 || v == 1.0 => Ok(v == 1.0),
DbValue::Text(v) if v == "false" || v == "true" => Ok(v == "true"),
value => Err(mismatch(value.kind(), std::any::type_name::<bool>())),
}
}
type Inner = bool;
}
pub struct Int;
impl TypeMeta for Int {
type Repr<'a> = i32;
}
impl Nullability for Int {
type Null = NotNull;
}
impl Required for Int {}
impl Comparable for Int {}
impl Orderable for Int {}
impl Numeric for Int {}
impl TypeCast for Int {
type From = i32;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
*value
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
let kind = value.kind();
let to = std::any::type_name::<i32>();
match value {
DbValue::Bool(v) => Ok(v.into()),
DbValue::Unsigned(v) => v
.try_into()
.map_err(|e| try_from_int(kind, to, e)),
DbValue::BigInt(v) => v
.try_into()
.map_err(|e| try_from_int(kind, to, e)),
DbValue::Text(v) => v
.parse::<i32>()
.map_err(|e| parse_int(kind, to, e)),
_ => Err(mismatch(kind, to)),
}
}
type Inner = i32;
}
pub struct BigInt;
impl TypeMeta for BigInt {
type Repr<'a> = i64;
}
impl Nullability for BigInt {
type Null = NotNull;
}
impl Required for BigInt {}
impl Comparable for BigInt {}
impl Orderable for BigInt {}
impl Numeric for BigInt {}
impl TypeCast for BigInt {
type From = i64;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
*value
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
let kind = value.kind();
let to = std::any::type_name::<i64>();
match value {
DbValue::Bool(v) => Ok(v.into()),
DbValue::Unsigned(v) => v
.try_into()
.map_err(|e| try_from_int(kind, to, e)),
DbValue::BigInt(v) => Ok(v),
DbValue::Text(v) => v
.parse::<i64>()
.map_err(|e| parse_int(kind, to, e)),
_ => Err(mismatch(kind, to)),
}
}
type Inner = i64;
}
pub struct UBigInt;
impl TypeMeta for UBigInt {
type Repr<'a> = u64;
}
impl Nullability for UBigInt {
type Null = NotNull;
}
impl Required for UBigInt {}
impl Comparable for UBigInt {}
impl Orderable for UBigInt {}
impl Numeric for UBigInt {}
impl TypeCast for UBigInt {
type From = u64;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
*value
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
let kind = value.kind();
let to = std::any::type_name::<u64>();
match value {
DbValue::Bool(v) => Ok(v.into()),
DbValue::Unsigned(v) => Ok(v),
DbValue::BigInt(v) => v
.try_into()
.map_err(|e| try_from_int(kind, to, e)),
DbValue::Text(v) => v
.parse::<u64>()
.map_err(|e| parse_int(kind, to, e)),
_ => Err(mismatch(kind, to)),
}
}
type Inner = u64;
}
pub struct UInt;
impl TypeMeta for UInt {
type Repr<'a> = u32;
}
impl Nullability for UInt {
type Null = NotNull;
}
impl Required for UInt {}
impl Comparable for UInt {}
impl Orderable for UInt {}
impl Numeric for UInt {}
impl TypeCast for UInt {
type From = u32;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
*value
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
let kind = value.kind();
let to = std::any::type_name::<u32>();
match value {
DbValue::Bool(v) => Ok(v.into()),
DbValue::Unsigned(v) => v
.try_into()
.map_err(|e| try_from_int(kind, to, e)),
DbValue::BigInt(v) => v
.try_into()
.map_err(|e| try_from_int(kind, to, e)),
DbValue::Text(v) => v
.parse::<u32>()
.map_err(|e| parse_int(kind, to, e)),
_ => Err(mismatch(kind, to)),
}
}
type Inner = u32;
}
pub struct Float;
impl TypeMeta for Float {
type Repr<'a> = f32;
}
impl Nullability for Float {
type Null = NotNull;
}
impl Required for Float {}
impl Comparable for Float {}
impl Orderable for Float {}
impl Numeric for Float {}
impl TypeCast for Float {
type From = f32;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
*value
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
let kind = value.kind();
let to = std::any::type_name::<f32>();
match value {
DbValue::Bool(v) => Ok((v as u8).into()),
DbValue::Unsigned(v) => {
let value: u16 = v
.try_into()
.map_err(|e| try_from_int(kind, to, e))?;
Ok(value.into())
}
DbValue::BigInt(v) => {
let value: i16 = v
.try_into()
.map_err(|e| try_from_int(kind, to, e))?;
Ok(value.into())
}
DbValue::Double(v) => {
if !v.is_finite() || v > f32::MAX as f64 || v < f32::MIN as f64 {
return Err(overflow(kind, to));
}
Ok(v as f32)
}
DbValue::Text(v) => v
.parse::<f32>()
.map_err(|e| parse_float(kind, to, e)),
_ => Err(mismatch(kind, to)),
}
}
type Inner = f32;
}
pub struct Double;
impl TypeMeta for Double {
type Repr<'a> = f64;
}
impl Nullability for Double {
type Null = NotNull;
}
impl Required for Double {}
impl Comparable for Double {}
impl Orderable for Double {}
impl Numeric for Double {}
impl TypeCast for Double {
type From = f64;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
*value
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
let kind = value.kind();
let to = std::any::type_name::<f64>();
match value {
DbValue::Bool(v) => Ok((v as u8).into()),
DbValue::Unsigned(v) => {
let value: u32 = v
.try_into()
.map_err(|e| try_from_int(kind, to, e))?;
Ok(value.into())
}
DbValue::BigInt(v) => {
let value: i32 = v
.try_into()
.map_err(|e| try_from_int(kind, to, e))?;
Ok(value.into())
}
DbValue::Double(v) => Ok(v),
DbValue::Text(v) => v
.parse::<f64>()
.map_err(|e| parse_float(kind, to, e)),
_ => Err(mismatch(kind, to)),
}
}
type Inner = f64;
}
pub struct Text;
impl TypeMeta for Text {
type Repr<'a> = Cow<'a, str>;
}
impl Nullability for Text {
type Null = NotNull;
}
impl Required for Text {}
impl Comparable for Text {}
impl Orderable for Text {}
impl Likeable for Text {}
impl TypeCast for Text {
type From = String;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
Cow::Borrowed(value.as_str())
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
let kind = value.kind();
let to = std::any::type_name::<String>();
match value {
DbValue::Text(v) => Ok(Cow::Owned(v)),
DbValue::Blob(v) => String::from_utf8(v)
.map(Cow::Owned)
.map_err(|e| utf8(kind, to, e)),
_ => Err(mismatch(kind, to)),
}
}
type Inner = TextSpan;
}
impl AsCast<Text> for Text {
type Cast<'a> = Cow<'a, str>;
fn as_cast<'a>(value: &'a String) -> Self::Cast<'a> {
Cow::Borrowed(value.as_str())
}
}
impl AsCast<Blob> for Text {
type Cast<'a> = Cow<'a, str>;
fn as_cast<'a>(value: &'a Vec<u8>) -> Self::Cast<'a> {
Cow::Owned(std::str::from_utf8(value).unwrap().to_owned())
}
}
pub struct Date;
impl TypeMeta for Date {
type Repr<'a> = Cow<'a, str>;
}
impl Nullability for Date {
type Null = NotNull;
}
impl Required for Date {}
impl Comparable for Date {}
impl Orderable for Date {}
impl TypeCast for Date {
type From = QuexDate;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
Cow::Owned(format!(
"{:04}-{:02}-{:02}",
value.year, value.month, value.day
))
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
Text::from_db_value(value)
}
type Inner = TextSpan;
}
pub struct Time;
impl TypeMeta for Time {
type Repr<'a> = Cow<'a, str>;
}
impl Nullability for Time {
type Null = NotNull;
}
impl Required for Time {}
impl Comparable for Time {}
impl Orderable for Time {}
impl TypeCast for Time {
type From = QuexTime;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
format_time(*value)
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
Text::from_db_value(value)
}
type Inner = TextSpan;
}
pub struct Timestamp;
impl TypeMeta for Timestamp {
type Repr<'a> = Cow<'a, str>;
}
impl Nullability for Timestamp {
type Null = NotNull;
}
impl Required for Timestamp {}
impl Comparable for Timestamp {}
impl Orderable for Timestamp {}
impl TypeCast for Timestamp {
type From = QuexDateTime;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
Cow::Owned(format!(
"{:04}-{:02}-{:02} {}",
value.date.year,
value.date.month,
value.date.day,
format_time(value.time)
))
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
Text::from_db_value(value)
}
type Inner = TextSpan;
}
fn format_time(value: QuexTime) -> Cow<'static, str> {
if value.microsecond == 0 {
Cow::Owned(format!(
"{:02}:{:02}:{:02}",
value.hour, value.minute, value.second
))
} else {
let mut micros = format!("{:06}", value.microsecond);
while micros.ends_with('0') {
micros.pop();
}
Cow::Owned(format!(
"{:02}:{:02}:{:02}.{}",
value.hour, value.minute, value.second, micros
))
}
}
pub struct Blob;
impl TypeMeta for Blob {
type Repr<'a> = Cow<'a, [u8]>;
}
impl Nullability for Blob {
type Null = NotNull;
}
impl Required for Blob {}
impl Comparable for Blob {}
impl Orderable for Blob {}
impl TypeCast for Blob {
type From = Vec<u8>;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
Cow::Borrowed(value.as_ref())
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
let kind = value.kind();
let to = std::any::type_name::<Vec<u8>>();
match value {
DbValue::Text(v) => Ok(Cow::Owned(v.into_bytes())),
DbValue::Blob(v) => Ok(Cow::Owned(v)),
_ => Err(mismatch(kind, to)),
}
}
type Inner = Span;
}
pub struct Nullable<T> {
marker: PhantomData<T>,
}
impl<T: Comparable + Required> Comparable for Nullable<T> {}
impl<T: Orderable + Required> Orderable for Nullable<T> {}
impl<T: Numeric + Required> Numeric for Nullable<T> {}
impl<T: Logical + Required> Logical for Nullable<T> {}
impl<T: Boolean + Required> Boolean for Nullable<T> {}
impl<T: Likeable + Required> Likeable for Nullable<T> {}
impl<T> TypeCast for Nullable<T>
where
T: TypeCast,
{
type From = Option<T::From>;
fn as_cast<'a>(value: &'a Self::From) -> Self::Repr<'a> {
value.as_ref().map(|v| T::as_cast(v))
}
fn from_db_value(value: DbValue) -> quex::Result<Self::Repr<'static>> {
match value {
DbValue::Null => Ok(None),
value => T::from_db_value(value).map(Some),
}
}
type Inner = Option<T::Inner>;
}
impl<T> TypeMeta for Nullable<T>
where
T: TypeMeta,
{
type Repr<'a> = Option<T::Repr<'a>>;
}
impl<T> Nullability for Nullable<T>
where
T: TypeMeta,
{
type Null = Null;
}
pub type NullOf<T> = <T as Nullability>::Null;
pub trait PredicateType {
type Output: Boolean;
}
pub struct UnaryType<L> {
marker: PhantomData<L>,
}
impl PredicateType for UnaryType<NotNull> {
type Output = Bool;
}
impl PredicateType for UnaryType<Null> {
type Output = Nullable<Bool>;
}
pub struct BinaryType<L, R> {
marker: PhantomData<(L, R)>,
}
impl PredicateType for BinaryType<NotNull, NotNull> {
type Output = Bool;
}
impl PredicateType for BinaryType<NotNull, Null> {
type Output = Nullable<Bool>;
}
impl PredicateType for BinaryType<Null, NotNull> {
type Output = Nullable<Bool>;
}
impl PredicateType for BinaryType<Null, Null> {
type Output = Nullable<Bool>;
}
pub struct TernaryType<P, Q, R> {
marker: PhantomData<(P, Q, R)>,
}
impl PredicateType for TernaryType<NotNull, NotNull, NotNull> {
type Output = Bool;
}
impl PredicateType for TernaryType<Null, NotNull, NotNull> {
type Output = Nullable<Bool>;
}
impl PredicateType for TernaryType<NotNull, Null, NotNull> {
type Output = Nullable<Bool>;
}
impl PredicateType for TernaryType<NotNull, NotNull, Null> {
type Output = Nullable<Bool>;
}
impl PredicateType for TernaryType<Null, Null, NotNull> {
type Output = Nullable<Bool>;
}
impl PredicateType for TernaryType<Null, NotNull, Null> {
type Output = Nullable<Bool>;
}
impl PredicateType for TernaryType<NotNull, Null, Null> {
type Output = Nullable<Bool>;
}
impl PredicateType for TernaryType<Null, Null, Null> {
type Output = Nullable<Bool>;
}
pub trait MathType {
type Output: Numeric;
}
macro_rules! binary_numeric {
($left:ty, $right:ty => $out:ty) => {
impl MathType for BinaryType<$left, $right> {
type Output = $out;
}
impl MathType for BinaryType<$right, $left> {
type Output = $out;
}
};
}
impl MathType for BinaryType<Int, Int> {
type Output = Int;
}
impl MathType for BinaryType<BigInt, BigInt> {
type Output = BigInt;
}
impl MathType for BinaryType<UInt, UInt> {
type Output = UInt;
}
impl MathType for BinaryType<UBigInt, UBigInt> {
type Output = UBigInt;
}
impl MathType for BinaryType<Float, Float> {
type Output = Float;
}
impl MathType for BinaryType<Double, Double> {
type Output = Double;
}
binary_numeric!(Int, BigInt => BigInt);
binary_numeric!(Int, Float => Float);
binary_numeric!(Int, Double => Double);
binary_numeric!(BigInt, Float => Float);
binary_numeric!(BigInt, Double => Double);
binary_numeric!(UInt, UBigInt => UBigInt);
binary_numeric!(UInt, Float => Float);
binary_numeric!(UInt, Double => Double);
binary_numeric!(UBigInt, Float => Float);
binary_numeric!(UBigInt, Double => Double);
binary_numeric!(Float, Double => Double);