use rust_decimal::prelude::ToPrimitive;
use rust_decimal::prelude::FromPrimitive;
use std::str::FromStr;
use crate::base::error::DatabaseError;
use crate::util::time_util;
use chrono::{DateTime, Local};
use rust_decimal::Decimal;
use paste::paste;
#[derive(Clone, Debug)]
pub enum ParamValue {
I8(i8),
I16(i16),
I32(i32),
I64(i64),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
F32(f32),
F64(f64),
Bool(bool),
String(String),
DateTime(DateTime<Local>),
Blob(Vec<u8>),
Clob(Vec<u8>),
Decimal(Decimal),
Null,
}
macro_rules! impl_param_value {
($($type:ty => $variant:ident),* $(,)?) => {
$(
impl From<$type> for ParamValue {
fn from(value: $type) -> Self {
ParamValue::$variant(value)
}
}
impl From<&$type> for ParamValue {
fn from(value: &$type) -> Self {
ParamValue::$variant(*value)
}
}
)*
};
(clone $($type:ty => $variant:ident),* $(,)?) => {
$(
impl From<$type> for ParamValue {
fn from(value: $type) -> Self {
ParamValue::$variant(value)
}
}
impl From<&$type> for ParamValue {
fn from(value: &$type) -> Self {
ParamValue::$variant(value.clone())
}
}
)*
};
(special $type:ty => $variant:ident, $convert:expr) => {
impl From<$type> for ParamValue {
fn from(value: $type) -> Self {
ParamValue::$variant($convert(value))
}
}
};
}
impl_param_value! {
i8 => I8,
i16 => I16,
i32 => I32,
i64 => I64,
u8 => U8,
u16 => U16,
u32 => U32,
u64 => U64,
f32 => F32,
f64 => F64,
bool => Bool,
}
impl_param_value!(clone
String => String,
DateTime<Local> => DateTime,
Vec<u8> => Blob,
Decimal => Decimal,
);
impl From<&str> for ParamValue {
fn from(s: &str) -> Self {
ParamValue::String(s.to_string())
}
}
pub fn get_param_value<T>(value: Option<T>) -> ParamValue
where
T: Into<ParamValue>,
{
value.map(Into::into).unwrap_or(ParamValue::Null)
}
pub fn get_param_value_ref<T>(value: &Option<T>) -> ParamValue
where
T: Clone + Into<ParamValue>,
{
value
.as_ref()
.map(|v| v.clone().into())
.unwrap_or(ParamValue::Null)
}
macro_rules! impl_numeric_conversions {
($target:ty: $src:ident) => {
impl From<ParamValue> for Option<$target> {
fn from(val: ParamValue) -> Self {
match val {
ParamValue::$src(v) => Some(v),
_ => None,
}
}
}
impl From<ParamValue> for $target where $target: Default {
fn from(val: ParamValue) -> Self {
let result: Option<$target> = val.into();
result.unwrap_or_default()
}
}
};
($target:ty: $($src:ident),+ $(,)?) => {
impl From<ParamValue> for $target where $target: Default{
fn from(val: ParamValue) -> Self {
let result: Option<$target> = val.into();
result.unwrap_or_default()
}
}
impl From<ParamValue> for Option<$target> {
fn from(val: ParamValue) -> Self {
match val {
$(
ParamValue::$src(v) => Some(v as $target),
)+
ParamValue::String(v) => Some(v.parse::<$target>().unwrap_or_default()),
ParamValue::Decimal(v) => paste! {
Some(v.[<to_ $target>]().unwrap_or_default())
},
ParamValue::Bool(v)=>{
if v {
Some(1 as $target)
} else {
Some(0 as $target)
}
}
_ => None,
}
}
}
impl From<&ParamValue> for Option<$target> {
fn from(val: &ParamValue) -> Self {
match val {
$(
ParamValue::$src(v) => Some(*v as $target),
)+
ParamValue::String(v) => Some(v.parse::<$target>().unwrap_or_default()),
ParamValue::Bool(v)=>{
if *v {
Some(1 as $target)
} else {
Some(0 as $target)
}
},
_ => None,
}
}
}
};
}
macro_rules! impl_float_conversions {
($target:ty: $($src:ident),+ $(,)?) => {
impl From<ParamValue> for Option<$target> {
fn from(val: ParamValue) -> Self {
match val {
$(
ParamValue::$src(v) => Some(v as $target),
)+
ParamValue::Decimal(v)=>Some(v.to_f64().unwrap_or_default() as $target),
ParamValue::String(v)=>Some(v.parse::<$target>().unwrap_or_default()),
_ => None,
}
}
}
impl From<ParamValue> for $target where $target: Default {
fn from(val: ParamValue) -> Self {
let result: Option<$target> = val.into();
result.unwrap_or_default()
}
}
};
}
macro_rules! impl_decimal_conversions {
($target:ty: $($src:ident),+ $(,)?) => {
impl From<ParamValue> for Option<$target> {
fn from(val: ParamValue) -> Self {
match val {
$(
ParamValue::$src(v) => Some(v.into()),
)+
ParamValue::F64(v)=>Decimal::from_f64(v),
ParamValue::F32(v)=>Decimal::from_f32(v),
ParamValue::Decimal(v)=>Some(v),
ParamValue::Blob(v)=>{
let res = String::from_utf8(v.to_vec());
if res.is_err(){
return None;
}
Some(Decimal::from_str(res.unwrap().as_str()).unwrap_or_default())
}
ParamValue::String(v)=>Some(Decimal::from_str(v.as_str()).unwrap_or_default()),
_ => None,
}
}
}
impl From<ParamValue> for $target where $target: Default {
fn from(val: ParamValue) -> Self {
let result: Option<$target> = val.into();
result.unwrap_or_default()
}
}
};
}
macro_rules! impl_bool_conversions {
(bool: $($src:ident),+ $(,)?) => {
impl From<ParamValue> for Option<bool> {
fn from(val: ParamValue) -> Self {
match val {
$(
ParamValue::$src(v) => {
if v > 0{
Some(true)
}else {
Some(false)
}
},
)+
ParamValue::Bool(v)=>{
Some(v)
},
ParamValue::String(v)=>{
Some(v.parse::<bool>().unwrap_or_default())
},
_ => None,
}
}
}
impl From<ParamValue> for bool where bool: Default {
fn from(val: ParamValue) -> Self {
let result: Option<bool> = val.into();
result.unwrap_or_default()
}
}
};
}
macro_rules! impl_date_conversions {
(date: $($src:ident),+ $(,)?) => {
impl From<ParamValue> for Option<DateTime<Local>> {
fn from(val: ParamValue) -> Self {
match val {
$(
ParamValue::$src(v) => {
Some(time_util::create_datetime_local_from_seconds(v as i64))
},
)+
ParamValue::String(v)=>{
let res = time_util::format_date_time_local_from_str(v.as_str(), "%Y-%m-%d %H:%M:%S");
if res.is_ok() {
return Some(res.unwrap());
}
None
},
ParamValue::DateTime(v)=>{
Some(v)
}
_ => None,
}
}
}
impl From<ParamValue> for DateTime<Local> where DateTime<Local>: Default {
fn from(val: ParamValue) -> Self {
let result: Option<DateTime<Local>> = val.into();
result.unwrap_or_default()
}
}
};
}
impl_numeric_conversions!(i8: I8, I16, I32, I64, U8, U16, U32, U64);
impl_numeric_conversions!(i16: I8, I16, I32, I64, U8, U16, U32, U64);
impl_numeric_conversions!(i32: I8, I16, I32, I64, U8, U16, U32, U64);
impl_numeric_conversions!(i64: I8, I16, I32, I64, U8, U16, U32, U64);
impl_numeric_conversions!(usize: I8, I16, I32, I64, U8, U16, U32, U64);
impl_numeric_conversions!(u8: I8, I16, I32, I64, U8, U16, U32, U64);
impl_numeric_conversions!(u16: I8, I16, I32, I64, U8, U16, U32, U64);
impl_numeric_conversions!(u32: I8, I16, I32, I64, U8, U16, U32, U64);
impl_numeric_conversions!(u64: I8, I16, I32, I64, U8, U16, U32, U64);
impl_float_conversions!(f32: F64, F32, I8, I16, I32, I64, U8, U16, U32, U64);
impl_float_conversions!(f64: F64, F32, I8, I16, I32, I64, U8, U16, U32, U64);
impl_decimal_conversions!(Decimal: I8, I16, I32, I64, U8, U16, U32, U64);
impl_bool_conversions!(bool: I8, I16, I32, I64, U8, U16, U32, U64);
impl From<ParamValue> for Option<String> {
fn from(val: ParamValue) -> Self {
if val.is_null() {
None
} else {
Some(val.to_string())
}
}
}
impl From<ParamValue> for String {
fn from(val: ParamValue) -> Self {
if val.is_null() {
"".to_string()
} else {
val.to_string()
}
}
}
impl_date_conversions!(date: I8, I16, I32, I64, U8, U16, U32, U64);
impl From<ParamValue> for Option<Vec<u8>> {
fn from(val: ParamValue) -> Self {
match val {
ParamValue::Blob(v) | ParamValue::Clob(v) => Some(v),
_ => None,
}
}
}
impl From<ParamValue> for Vec<u8> {
fn from(val: ParamValue) -> Self {
match val {
ParamValue::Blob(v) | ParamValue::Clob(v) => v,
_ => Vec::new(),
}
}
}
impl ParamValue {
pub fn is_null(&self) -> bool {
match self {
ParamValue::Null => true,
_ => false,
}
}
pub fn is_not_null(&self) -> bool {
!self.is_null()
}
pub fn to_string(&self) -> String {
match self {
ParamValue::U64(x) => x.to_string(),
ParamValue::U32(x) => x.to_string(),
ParamValue::U16(x) => x.to_string(),
ParamValue::U8(x) => x.to_string(),
ParamValue::I64(x) => x.to_string(),
ParamValue::I32(x) => x.to_string(),
ParamValue::I16(x) => x.to_string(),
ParamValue::I8(x) => x.to_string(),
ParamValue::Bool(x) => x.to_string(),
ParamValue::String(x) => x.to_string(),
ParamValue::DateTime(x) => x.to_string(),
ParamValue::Blob(x) => String::from_utf8(x.to_vec()).unwrap_or_default(),
ParamValue::Clob(x) => String::from_utf8(x.to_vec()).unwrap_or_default(),
ParamValue::Null => "null".to_string(),
ParamValue::F32(x) => x.to_string(),
ParamValue::F64(x) => x.to_string(),
ParamValue::Decimal(x) => x.to_string(),
}
}
pub fn as_option<T>(self) -> Option<T>
where
Self: Into<Option<T>>,
{
self.into()
}
pub fn try_as<T>(self) -> Result<T, DatabaseError>
where
Self: TryInto<T, Error = DatabaseError>,
{
self.try_into()
}
pub fn as_number_or<T>(self, default: T) -> T
where
T: Default,
Self: Into<Option<T>>,
{
self.into().unwrap_or(default)
}
pub fn can_convert_to<T>(&self) -> bool
where
Self: Clone + Into<Option<T>>,
{
self.clone().into().is_some()
}
}
pub trait IntoParamValue {
fn into_param_value(self) -> ParamValue;
}
impl<T> IntoParamValue for T
where
T: Into<ParamValue>,
{
fn into_param_value(self) -> ParamValue {
self.into()
}
}