use std::any::Any;
use std::borrow::Cow;
use chrono::{DateTime, Utc};
use uuid::Uuid;
use serde_json::Value;
#[derive(Debug, Clone)]
pub enum DataKind<'a> {
Text(Cow<'a, str>),
TextNone,
Boolean(bool),
BooleanNone,
Integer(i32),
IntegerNone,
Long(i64),
LongNone,
Float(f32),
FloatNone,
Double(f64),
DoubleNone,
DateTime(DateTime<Utc>),
DateTimeNone,
Uuid(Uuid),
UuidNone,
Blob(Cow<'a, [u8]>),
BlobNone,
Json(Cow<'a, Value>),
JsonNone,
Enum(Cow<'a, str>),
EnumNone,
Set(Cow<'a, str>),
SetNone,
Unsupported,
}
fn unwrap_option<T: Any>(value: &dyn Any) -> Option<&T> {
if value.is::<Option<T>>() {
let opt = value.downcast_ref::<Option<T>>();
match opt.and_then(|x| x.as_ref()) {
Some(inner) => Some(inner),
None => None,
}
} else if value.is::<T>() {
value.downcast_ref::<T>()
} else {
None
}
}
pub fn value_convert<'a>(value: &dyn Any) -> DataKind<'a> {
if let Some(s) = unwrap_option::<String>(value) {
DataKind::Text(Cow::Owned(s.clone()))
} else if let Some(s) = unwrap_option::<&str>(value) {
DataKind::Text(Cow::Borrowed(s))
} else if let Some(b) = unwrap_option::<bool>(value) {
DataKind::Boolean(*b)
} else if let Some(i) = unwrap_option::<i32>(value) {
DataKind::Integer(*i)
} else if let Some(l) = unwrap_option::<i64>(value) {
DataKind::Long(*l)
} else if let Some(f) = unwrap_option::<f32>(value) {
DataKind::Float(*f)
} else if let Some(d) = unwrap_option::<f64>(value) {
DataKind::Double(*d)
} else if let Some(dt) = unwrap_option::<DateTime<Utc>>(value) {
DataKind::DateTime(*dt)
} else if let Some(uuid) = unwrap_option::<Uuid>(value) {
DataKind::Uuid(*uuid)
} else if let Some(blob) = unwrap_option::<Vec<u8>>(value) {
DataKind::Blob(Cow::Owned(blob.clone()))
} else if let Some(blob) = unwrap_option::<&[u8]>(value) {
DataKind::Blob(Cow::Borrowed(blob))
} else if let Some(json) = unwrap_option::<Value>(value) {
DataKind::Json(Cow::Owned(json.clone()))
} else {
if value.is::<Option<String>>() {
DataKind::TextNone
} else if value.is::<Option<bool>>() {
DataKind::BooleanNone
} else if value.is::<Option<i32>>() {
DataKind::IntegerNone
} else if value.is::<Option<i64>>() {
DataKind::LongNone
} else if value.is::<Option<f32>>() {
DataKind::FloatNone
} else if value.is::<Option<f64>>() {
DataKind::DoubleNone
} else if value.is::<Option<DateTime<Utc>>>() {
DataKind::DateTimeNone
} else if value.is::<Option<Uuid>>() {
DataKind::UuidNone
} else if value.is::<Option<Vec<u8>>>() {
DataKind::BlobNone
} else if value.is::<Option<Value>>() {
DataKind::JsonNone
} else {
DataKind::Unsupported
}
}
}
pub fn is_empty(value: &dyn Any) -> bool {
if let Some(s) = unwrap_option::<String>(value) {
return s.is_empty() || s.to_lowercase() == "null";
}
if let Some(s) = unwrap_option::<&str>(value) {
return s.is_empty() || s.to_lowercase() == "null";
}
if let Some(blob) = unwrap_option::<Vec<u8>>(value) {
return blob.is_empty();
}
if let Some(blob) = unwrap_option::<&[u8]>(value) {
return blob.is_empty();
}
if value.is::<Option<String>>()
|| value.is::<Option<bool>>()
|| value.is::<Option<i32>>()
|| value.is::<Option<i64>>()
|| value.is::<Option<f32>>()
|| value.is::<Option<f64>>()
|| value.is::<Option<DateTime<Utc>>>()
|| value.is::<Option<Uuid>>()
|| value.is::<Option<Vec<u8>>>()
|| value.is::<Option<Value>>() {
return unwrap_option::<()>(value).is_none();
}
false
}
impl<'a> DataKind<'a> {
pub fn as_enum(item: impl Into<Cow<'a, str>>) -> Self {
DataKind::Enum(item.into())
}
pub fn as_set(item: impl Into<Cow<'a, str>>) -> Self {
DataKind::Set(item.into())
}
}
impl<'a> From<Cow<'a, str>> for DataKind<'a> {
fn from(item: Cow<'a, str>) -> Self {
DataKind::Text(item)
}
}
impl From<String> for DataKind<'_> {
fn from(item: String) -> Self {
DataKind::Text(Cow::Owned(item))
}
}
impl<'a> From<&'a str> for DataKind<'a> {
fn from(item: &'a str) -> Self {
DataKind::Text(Cow::Borrowed(item))
}
}
impl From<bool> for DataKind<'_> {
fn from(item: bool) -> Self {
DataKind::Boolean(item)
}
}
impl From<i32> for DataKind<'_> {
fn from(item: i32) -> Self {
DataKind::Integer(item)
}
}
impl From<i64> for DataKind<'_> {
fn from(item: i64) -> Self {
DataKind::Long(item)
}
}
impl From<f32> for DataKind<'_> {
fn from(item: f32) -> Self {
DataKind::Float(item)
}
}
impl From<f64> for DataKind<'_> {
fn from(item: f64) -> Self {
DataKind::Double(item)
}
}
#[macro_export]
macro_rules! mysql_field_bind {
($query:expr, $value:expr) => {{
use crate::mysql::kind::DataKind::*;
use chrono::{DateTime, Utc};
use serde_json::Value;
use sqlx::Error;
match $value {
Text(s) => $query.bind(s.into_owned()),
Boolean(b) => $query.bind(b),
Integer(n) => $query.bind(n),
Long(n) => $query.bind(n),
Float(n) => $query.bind(n),
Double(n) => $query.bind(n),
DateTime(dt) => $query.bind(dt),
Uuid(uuid) => $query.bind(uuid.to_string()),
Blob(b) => $query.bind(b.into_owned()),
Json(json) => $query.bind(json.into_owned()),
Enum(s) => $query.bind(s.into_owned()),
Set(s) => $query.bind(s.into_owned()),
TextNone => $query.bind(None::<String>),
BooleanNone => $query.bind(None::<bool>),
IntegerNone => $query.bind(None::<i32>),
LongNone => $query.bind(None::<i64>),
FloatNone => $query.bind(None::<f32>),
DoubleNone => $query.bind(None::<f64>),
DateTimeNone => $query.bind(None::<DateTime<Utc>>),
UuidNone => $query.bind(None::<String>),
BlobNone => $query.bind(None::<Vec<u8>>),
JsonNone => $query.bind(None::<Value>),
EnumNone => $query.bind(None::<String>),
SetNone => $query.bind(None::<String>),
Unsupported => return Err(Error::Decode("Unsupported data type encountered".into())),
}
}};
}