use crate::{FilterValue, Params, SqlxError};
use sqlx_data_integration::{
Arguments, BoxDynError, DB, Database, DbArgumentBuffer, DbArguments, Encode, IntoArguments,
IsNull, Result, Type, TypeInfo,
};
use sqlx_data_integration::{
BString, BitVec, Date, DateTime, Decimal, IpNet, IpNetwork,
MacAddress, NaiveDate, NaiveDateTime, NaiveTime, PrimitiveDateTime, Time, Uuid,
};
#[cfg(feature = "json")]
type Json = sqlx_data_integration::Json<sqlx_data_integration::JsonValue>;
#[cfg(not(feature = "json"))]
type Json = String;
fn bind_filter_values<'q, DB>(values: &'q [FilterValue]) -> Result<DbArguments<'q>>
where
DB: Database,
FilterValue: Encode<'q, DB> + Type<DB>,
{
let mut args = DbArguments::default();
for v in values {
match v {
FilterValue::String(s) => args.add(s).map_err(|e| {
SqlxError::Encode(format!("Failed to bind FilterValue: {}", e).into())
})?,
FilterValue::Int(i) => args.add(*i).map_err(|e| {
SqlxError::Encode(format!("Failed to bind FilterValue: {}", e).into())
})?,
#[cfg(any(feature = "sqlite", feature = "postgres"))]
FilterValue::UInt(u) => args.add(*u as i64).map_err(|e| {
SqlxError::Encode(format!("Failed to bind FilterValue: {}", e).into())
})?,
#[cfg(feature = "mysql")]
FilterValue::UInt(u) => args.add(*u).map_err(|e| {
SqlxError::Encode(format!("Failed to bind FilterValue: {}", e).into())
})?,
#[cfg(any(feature = "sqlite", feature = "mysql"))]
FilterValue::Bool(b) => args.add(*b as i64).map_err(|e| {
SqlxError::Encode(format!("Failed to bind FilterValue: {}", e).into())
})?,
FilterValue::Float(f) => args.add(*f).map_err(|e| {
SqlxError::Encode(format!("Failed to bind FilterValue: {}", e).into())
})?,
FilterValue::Null => args.add::<Option<i32>>(None).map_err(|e| {
SqlxError::Encode(format!("Failed to bind FilterValue: {}", e).into())
})?,
FilterValue::Array(_) => {
return Err(SqlxError::Encode("Array binding not supported".into()));
}
_ => args.add(v).map_err(|e| {
SqlxError::Encode(format!("Failed to bind FilterValue: {}", e).into())
})?,
}
}
Ok(args)
}
#[cfg(any(feature = "mysql", feature = "postgres"))]
impl<'q> IntoArguments<'q, DB> for FilterValue {
#[allow(clippy::expect_used)]
fn into_arguments(self) -> DbArguments<'q> {
bind_filter_values::<DB>(&[self]).expect("unsupported argument in filter value.")
}
}
#[cfg(feature = "sqlite")]
impl<'q> IntoArguments<'q, DB> for FilterValue {
#[allow(clippy::expect_used)]
fn into_arguments(self) -> DbArguments<'q> {
let mut args = DbArguments::default();
let result = match self {
FilterValue::String(s) => args.add(s),
FilterValue::Int(i) => args.add(i),
FilterValue::UInt(u) => args.add(u as i64),
FilterValue::Bool(b) => args.add(b as i64),
FilterValue::Float(f) => args.add(f),
FilterValue::Null => args.add::<Option<i32>>(None),
FilterValue::Array(_) => panic!("Array binding not supported"),
_ => args.add(self),
};
result.expect("Failed to bind FilterValue");
args
}
}
impl FilterValue {
pub fn build_arguments<'q, DB>(filters: &'q [FilterValue]) -> Result<DbArguments<'q>>
where
DB: Database,
FilterValue: Encode<'q, DB> + Type<DB>,
{
bind_filter_values::<DB>(filters)
}
}
impl Params {
pub fn build_arguments<'q>(
&'q self,
bind_values: &'q [FilterValue],
) -> impl Fn() -> Result<DbArguments<'q>> + 'q {
move || bind_filter_values::<DB>(bind_values)
}
}
impl<'q> Encode<'q, DB> for FilterValue
where
String: Encode<'q, DB>,
i64: Encode<'q, DB>,
f64: Encode<'q, DB>,
bool: Encode<'q, DB>,
Vec<u8>: Encode<'q, DB>,
Json: Encode<'q, DB>,
Uuid: Encode<'q, DB>,
DateTime: Encode<'q, DB>,
NaiveDateTime: Encode<'q, DB>,
NaiveDate: Encode<'q, DB>,
NaiveTime: Encode<'q, DB>,
PrimitiveDateTime: Encode<'q, DB>,
Date: Encode<'q, DB>,
Time: Encode<'q, DB>,
Decimal: Encode<'q, DB>,
IpNet: Encode<'q, DB>,
IpNetwork: Encode<'q, DB>,
BitVec: Encode<'q, DB>,
BString: Encode<'q, DB>,
MacAddress: Encode<'q, DB>,
{
fn encode_by_ref(&self, args: &mut DbArgumentBuffer<'q>) -> Result<IsNull, BoxDynError> {
match self {
FilterValue::String(s) => s.encode_by_ref(args),
FilterValue::Int(i) => i.encode_by_ref(args),
FilterValue::UInt(i) => (*i as i64).encode_by_ref(args),
FilterValue::Float(f) => f.encode_by_ref(args),
FilterValue::Bool(b) => b.encode_by_ref(args),
FilterValue::Blob(v) => v.encode_by_ref(args),
FilterValue::Null => Ok(IsNull::Yes),
FilterValue::Array(_) => unreachable!("Array not supported in bind"),
#[cfg(feature = "json")]
FilterValue::Json(s) => s.encode_by_ref(args),
#[cfg(feature = "uuid")]
FilterValue::Uuid(uuid) => uuid.encode_by_ref(args),
#[cfg(feature = "chrono")]
FilterValue::DateTimeChrono(dt) => dt.encode_by_ref(args),
#[cfg(feature = "chrono")]
FilterValue::NaiveDateTime(ndt) => ndt.encode_by_ref(args),
#[cfg(feature = "chrono")]
FilterValue::NaiveDate(nd) => nd.encode_by_ref(args),
#[cfg(feature = "chrono")]
FilterValue::NaiveTime(nt) => nt.encode_by_ref(args),
#[cfg(all(feature = "time", not(feature = "chrono")))]
FilterValue::OffsetDateTime(odt) => odt.encode_by_ref(args),
#[cfg(all(feature = "time", not(feature = "chrono")))]
FilterValue::PrimitiveDateTime(pdt) => pdt.encode_by_ref(args),
#[cfg(all(feature = "time", not(feature = "chrono")))]
FilterValue::Date(date) => date.encode_by_ref(args),
#[cfg(all(feature = "time", not(feature = "chrono")))]
FilterValue::Time(time) => time.encode_by_ref(args),
#[cfg(feature = "rust_decimal")]
FilterValue::Decimal(decimal) => decimal.encode_by_ref(args),
#[cfg(feature = "bigdecimal")]
FilterValue::Decimal(decimal) => decimal.encode_by_ref(args),
#[cfg(feature = "ipnet")]
FilterValue::IpNet(ipnet) => ipnet.encode_by_ref(args),
#[cfg(feature = "ipnet")]
FilterValue::Ipv4Net(ipv4) => IpNet::from(*ipv4).encode_by_ref(args),
#[cfg(feature = "ipnet")]
FilterValue::Ipv6Net(ipv6) => IpNet::from(*ipv6).encode_by_ref(args),
#[cfg(feature = "ipnetwork")]
FilterValue::IpNetwork(ipnet) => ipnet.encode_by_ref(args),
#[cfg(feature = "mac_address")]
FilterValue::MacAddress(mac) => mac.encode_by_ref(args),
#[cfg(feature = "bit-vec")]
FilterValue::BitVec(bits) => bits.encode_by_ref(args),
#[cfg(feature = "bstr")]
FilterValue::BStr(bstr) => bstr.as_bytes().encode_by_ref(args),
#[cfg(feature = "regexp")]
FilterValue::Regex(pattern) => pattern.encode_by_ref(args),
}
}
}
impl Type<DB> for FilterValue
where
String: Type<DB>,
i64: Type<DB>,
f64: Type<DB>,
bool: Type<DB>,
Vec<u8>: Type<DB>,
Json: Type<DB>,
Uuid: Type<DB>,
DateTime: Type<DB>,
NaiveDateTime: Type<DB>,
NaiveDate: Type<DB>,
NaiveTime: Type<DB>,
PrimitiveDateTime: Type<DB>,
Date: Type<DB>,
Time: Type<DB>,
Decimal: Type<DB>,
IpNet: Type<DB>,
IpNetwork: Type<DB>,
BitVec: Type<DB>,
BString: Type<DB>,
MacAddress: Type<DB>,
{
fn type_info() -> TypeInfo {
<i64 as Type<DB>>::type_info()
}
fn compatible(ty: &TypeInfo) -> bool {
<String as Type<DB>>::compatible(ty)
|| <i64 as Type<DB>>::compatible(ty)
|| <f64 as Type<DB>>::compatible(ty)
|| <bool as Type<DB>>::compatible(ty)
|| <Vec<u8> as Type<DB>>::compatible(ty)
|| <Json as Type<DB>>::compatible(ty)
|| <Uuid as Type<DB>>::compatible(ty)
|| <DateTime as Type<DB>>::compatible(ty)
|| <NaiveDateTime as Type<DB>>::compatible(ty)
|| <NaiveDate as Type<DB>>::compatible(ty)
|| <NaiveTime as Type<DB>>::compatible(ty)
|| <PrimitiveDateTime as Type<DB>>::compatible(ty)
|| <Date as Type<DB>>::compatible(ty)
|| <Time as Type<DB>>::compatible(ty)
|| <Decimal as Type<DB>>::compatible(ty)
|| <IpNet as Type<DB>>::compatible(ty)
|| <IpNetwork as Type<DB>>::compatible(ty)
|| <BitVec as Type<DB>>::compatible(ty)
|| <BString as Type<DB>>::compatible(ty)
|| <MacAddress as Type<DB>>::compatible(ty)
}
}