use std::any::Any;
use std::borrow::Cow;
use std::error::Error;
use std::sync::Arc;
use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
use serde_json::Value;
use sqlx::encode::IsNull;
use sqlx::types::Uuid;
use sqlx::{Database, Encode, Sqlite, Type};
use sqlx::sqlite::SqliteArgumentValue;
use crate::common::conversion::{unwrap_option, ValueConvert};
#[derive(Default, Debug, Clone, PartialEq)]
pub enum DataKind {
Text(String),
Integer(i64),
Real(f64),
DateTime(NaiveDateTime), DateTimeUtc(DateTime<Utc>), Date(NaiveDate), Time(NaiveTime),
Blob(Arc<[u8]>),
Bool(bool),
Json(Arc<Value>),
Uuid(Uuid),
#[default]
Null, }
impl Encode<'_, Sqlite> for DataKind {
fn encode_by_ref(&self, buf: &mut Vec<SqliteArgumentValue<'_>>) -> Result<IsNull, Box<dyn Error + Send + Sync + 'static>> {
match self {
DataKind::Null => Ok(IsNull::Yes),
DataKind::Text(s) => <String as Encode<'_, Sqlite>>::encode(s.to_string(), buf),
DataKind::Integer(i) => <i64 as Encode<'_, Sqlite>>::encode(*i, buf),
DataKind::Real(r) => <f64 as Encode<'_, Sqlite>>::encode(*r, buf),
DataKind::DateTime(dt) => {
let utc_datetime = Utc.from_utc_datetime(dt);
<String as Encode<'_, Sqlite>>::encode(utc_datetime.to_rfc3339(), buf)
},
DataKind::DateTimeUtc(dt_utc) => <String as Encode<'_, Sqlite>>::encode(dt_utc.to_rfc3339(), buf),
DataKind::Date(date) => <String as Encode<'_, Sqlite>>::encode(date.format("%Y-%m-%d").to_string(), buf),
DataKind::Time(time) => <String as Encode<'_, Sqlite>>::encode(time.format("%H:%M:%S%.f").to_string(), buf),
DataKind::Blob(blob) => {
let owned_blob = Vec::from(blob.as_ref());
<Vec<u8> as Encode<'_, Sqlite>>::encode(owned_blob, buf)
}
DataKind::Bool(b) => <i64 as Encode<'_, Sqlite>>::encode(*b as i64, buf),
DataKind::Json(json) => {
let json_str = serde_json::to_string(json.as_ref())
.map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync + 'static>)?;
<String as Encode<'_, Sqlite>>::encode(json_str, buf)
},
DataKind::Uuid(uuid) => <String as Encode<'_, Sqlite>>::encode(uuid.to_string(), buf),
}
}
}
impl Type<Sqlite> for DataKind {
fn type_info() -> <Sqlite as Database>::TypeInfo {
<str as Type<Sqlite>>::type_info()
}
fn compatible(_: &<Sqlite as Database>::TypeInfo) -> bool {
true
}
}
impl ValueConvert for DataKind {
fn convert(value: &dyn Any) -> Self {
macro_rules! try_convert {
($($type:ty => $variant:expr),*) => {
$(if let Some(v) = unwrap_option::<$type>(value) {
return $variant(v);
})*
return DataKind::Null;
};
}
try_convert!(
String => |v: &String| DataKind::Text(v.to_string()),
&str => |v: &&str| DataKind::Text((*v).to_string()),
i32 => |v: &i32| DataKind::Integer(*v as i64),
u32 => |v: &u32| DataKind::Integer(*v as i64),
u64 => |v: &u64| DataKind::Integer(*v as i64),
i64 => |v: &i64| DataKind::Integer(*v),
f32 => |v: &f32| DataKind::Real(*v as f64),
f64 => |v: &f64| DataKind::Real(*v),
bool => |v: &bool| DataKind::Bool(*v),
NaiveDateTime => |v: &NaiveDateTime| DataKind::DateTime(*v),
DateTime<Utc> => |v: &DateTime<Utc>| DataKind::DateTimeUtc(*v),
NaiveDate => |v: &NaiveDate| DataKind::Date(*v),
NaiveTime => |v: &NaiveTime| DataKind::Time(*v),
Vec<u8> => |v: &Vec<u8>| DataKind::Blob(Arc::from(&**v)),
&[u8] => |v: &&[u8]| DataKind::Blob(Arc::from(*v)),
Value => |v: &Value| DataKind::Json(Arc::new(v.clone())),
Uuid => |v: &Uuid| DataKind::Uuid(*v)
);
}
fn is_default_value(value: &Self) -> bool {
match value {
DataKind::Integer(v) => *v == 0,
DataKind::Text(v) => v.is_empty(),
DataKind::Uuid(v) => v.is_nil(),
_ => false,
}
}
}
macro_rules! impl_from {
($type:ty, $variant:expr) => {
impl From<$type> for DataKind {
fn from(item: $type) -> Self {
$variant(item)
}
}
};
}
impl_from!(String, |value: String| DataKind::Text(value));
impl_from!(&str, |value: &str| DataKind::Text(value.to_string()));
impl_from!(Vec<u8>, |value: Vec<u8>| DataKind::Blob(Arc::from(value)));
impl_from!(&[u8], |value: &[u8]| DataKind::Blob(Arc::from(value)));
impl_from!(i32, |value: i32| DataKind::Integer(value as i64));
impl_from!(u32, |value: u32| DataKind::Integer(value as i64));
impl_from!(u64, |value: u64| DataKind::Integer(value as i64));
impl_from!(i64, DataKind::Integer);
impl_from!(f32, |value: f32| DataKind::Real(value as f64));
impl_from!(f64, DataKind::Real);
impl_from!(bool, DataKind::Bool);
impl_from!(NaiveDateTime, DataKind::DateTime);
impl_from!(DateTime<Utc>, DataKind::DateTimeUtc);
impl_from!(NaiveDate, DataKind::Date);
impl_from!(NaiveTime, DataKind::Time);
impl_from!(Value, |value: Value| DataKind::Json(Arc::new(value)));
impl_from!(Uuid, DataKind::Uuid);
impl<'a> From<DataKind> for Cow<'a, DataKind> {
fn from(value: DataKind) -> Self {
Cow::Owned(value)
}
}
impl<'a> From<&'a DataKind> for Cow<'a, DataKind> {
fn from(value: &'a DataKind) -> Self {
Cow::Borrowed(value)
}
}