use turso_orm_driver::FromValue;
use turso_sql::{ColumnType, Value};
pub trait TursoType: Sized + Clone + Send + Sync + Into<Value> + FromValue + 'static {
const COLUMN_TYPE: ColumnType;
const NULLABLE: bool = false;
const INTEGRAL: bool = false;
}
macro_rules! simple {
($($t:ty => $ct:expr, $integral:literal);* $(;)?) => {$(
impl TursoType for $t {
const COLUMN_TYPE: ColumnType = $ct;
const INTEGRAL: bool = $integral;
}
)*};
}
simple! {
bool => ColumnType::Boolean, false;
i8 => ColumnType::Integer, true;
i16 => ColumnType::Integer, true;
i32 => ColumnType::Integer, true;
i64 => ColumnType::Integer, true;
u8 => ColumnType::Integer, true;
u16 => ColumnType::Integer, true;
u32 => ColumnType::Integer, true;
f32 => ColumnType::Real, false;
f64 => ColumnType::Real, false;
String => ColumnType::Text, false;
Vec<u8> => ColumnType::Blob, false;
}
#[cfg(feature = "with-chrono")]
simple! {
chrono::NaiveDate => ColumnType::Date, false;
chrono::NaiveTime => ColumnType::Time, false;
chrono::NaiveDateTime => ColumnType::DateTime, false;
chrono::DateTime<chrono::Utc> => ColumnType::TimestampWithTimeZone, false;
chrono::DateTime<chrono::FixedOffset> => ColumnType::TimestampWithTimeZone, false;
}
#[cfg(feature = "with-uuid")]
simple! {
uuid::Uuid => ColumnType::Uuid, false;
}
#[cfg(feature = "with-json")]
simple! {
serde_json::Value => ColumnType::Json, false;
}
#[cfg(feature = "with-rust_decimal")]
simple! {
rust_decimal::Decimal => ColumnType::Decimal, false;
}
impl<T: TursoType> TursoType for Option<T> {
const COLUMN_TYPE: ColumnType = T::COLUMN_TYPE;
const NULLABLE: bool = true;
const INTEGRAL: bool = T::INTEGRAL;
}
pub trait TryFromU64: Sized {
fn try_from_u64(n: u64) -> crate::Result<Self>;
}
macro_rules! try_from_u64 {
($($t:ty),*) => {$(
impl TryFromU64 for $t {
fn try_from_u64(n: u64) -> crate::Result<Self> {
<$t>::try_from(n).map_err(|_| crate::DbErr::Type(format!("row id {n} does not fit in {}", stringify!($t))))
}
}
)*};
}
try_from_u64!(i8, i16, i32, i64, u8, u16, u32, u64);
macro_rules! no_try_from_u64 {
($($t:ty),*) => {$(
impl TryFromU64 for $t {
fn try_from_u64(_: u64) -> crate::Result<Self> {
Err(crate::DbErr::Type(format!("{} primary keys are not generated by the database", stringify!($t))))
}
}
)*};
}
no_try_from_u64!(String, Vec<u8>, bool, f32, f64);
#[cfg(feature = "with-uuid")]
no_try_from_u64!(uuid::Uuid);
macro_rules! tuple_try_from_u64 {
($(($($t:ident),+)),* $(,)?) => {$(
impl<$($t: TryFromU64),+> TryFromU64 for ($($t,)+) {
fn try_from_u64(_: u64) -> crate::Result<Self> {
Err(crate::DbErr::Type(
"composite primary keys are not generated by the database".into(),
))
}
}
)*};
}
tuple_try_from_u64!(
(A, B),
(A, B, C),
(A, B, C, D),
(A, B, C, D, E),
(A, B, C, D, E, F)
);
pub trait IntoValueTuple {
fn into_value_tuple(self) -> Vec<Value>;
}
macro_rules! single_value_tuple {
($($t:ty),*) => {$(
impl IntoValueTuple for $t {
fn into_value_tuple(self) -> Vec<Value> {
vec![self.into()]
}
}
)*};
}
single_value_tuple!(
bool,
i8,
i16,
i32,
i64,
u8,
u16,
u32,
f32,
f64,
String,
&str,
Vec<u8>,
Value
);
#[cfg(feature = "with-uuid")]
single_value_tuple!(uuid::Uuid);
#[cfg(feature = "with-chrono")]
single_value_tuple!(
chrono::NaiveDate,
chrono::NaiveDateTime,
chrono::DateTime<chrono::Utc>
);
macro_rules! tuple_into_value_tuple {
($(($($t:ident $i:tt),+)),* $(,)?) => {$(
impl<$($t: Into<Value>),+> IntoValueTuple for ($($t,)+) {
fn into_value_tuple(self) -> Vec<Value> {
vec![$(self.$i.into()),+]
}
}
)*};
}
tuple_into_value_tuple!(
(A 0, B 1),
(A 0, B 1, C 2),
(A 0, B 1, C 2, D 3),
(A 0, B 1, C 2, D 3, E 4),
(A 0, B 1, C 2, D 3, E 4, F 5),
);