#![warn(missing_docs)]
#[cfg(all(feature = "sqlx-0_8", feature = "sqlx-0_9"))]
compile_error!("Enable exactly one SQLx version feature: `sqlx-0_8` or `sqlx-0_9`.");
#[cfg(not(any(feature = "sqlx-0_8", feature = "sqlx-0_9")))]
compile_error!("Enable one SQLx version feature: `sqlx-0_8` or `sqlx-0_9`.");
#[cfg(feature = "sqlx-0_8")]
use sqlx08 as sqlx;
#[cfg(feature = "sqlx-0_9")]
use sqlx09 as sqlx;
pub use vld;
#[cfg(feature = "sqlx-0_8")]
type SqlxArgBuf<'q, DB> = <DB as sqlx::Database>::ArgumentBuffer<'q>;
#[cfg(feature = "sqlx-0_9")]
type SqlxArgBuf<'q, DB> = <DB as sqlx::Database>::ArgumentBuffer;
vld_integration_common::__vld_db_error!(
VldSqlxError
);
impl From<VldSqlxError> for sqlx::Error {
fn from(e: VldSqlxError) -> Self {
sqlx::Error::Encode(Box::new(e))
}
}
vld_integration_common::__vld_db_validated!(VldSqlxError);
vld_integration_common::__vld_db_wrappers! {
VldSqlxError;
text: []
int: []
float: []
bool: []
}
pub fn validate_insert<S, T>(value: &T) -> Result<(), VldSqlxError>
where
S: vld::schema::VldParse,
T: serde::Serialize,
{
__vld_validate::<S, T>(value).map(|_| ())
}
#[deprecated(
since = "0.5.0",
note = "identical to `validate_insert`; use it instead"
)]
pub fn validate_update<S, T>(value: &T) -> Result<(), VldSqlxError>
where
S: vld::schema::VldParse,
T: serde::Serialize,
{
validate_insert::<S, T>(value)
}
pub fn validate_row<S, T>(value: &T) -> Result<(), VldSqlxError>
where
S: vld::schema::VldParse,
T: serde::Serialize,
{
validate_insert::<S, T>(value)
}
pub fn validate_rows<S, T>(rows: &[T]) -> Result<(), (usize, VldSqlxError)>
where
S: vld::schema::VldParse,
T: serde::Serialize,
{
for (i, row) in rows.iter().enumerate() {
validate_row::<S, T>(row).map_err(|e| (i, e))?;
}
Ok(())
}
macro_rules! sqlx_delegate {
($Name:ident, $inner:ty) => {
impl<S, DB: sqlx::Database> sqlx::Type<DB> for $Name<S>
where
$inner: sqlx::Type<DB>,
{
fn type_info() -> DB::TypeInfo {
<$inner as sqlx::Type<DB>>::type_info()
}
fn compatible(ty: &DB::TypeInfo) -> bool {
<$inner as sqlx::Type<DB>>::compatible(ty)
}
}
impl<'q, S, DB: sqlx::Database> sqlx::Encode<'q, DB> for $Name<S>
where
$inner: sqlx::Encode<'q, DB>,
{
fn encode_by_ref(
&self,
buf: &mut SqlxArgBuf<'q, DB>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
self.value.encode_by_ref(buf)
}
}
impl<'r, S, DB: sqlx::Database> sqlx::Decode<'r, DB> for $Name<S>
where
$inner: sqlx::Decode<'r, DB>,
{
fn decode(
value: <DB as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
Ok(Self::new_unchecked(
<$inner as sqlx::Decode<'r, DB>>::decode(value)?,
))
}
}
};
}
sqlx_delegate!(VldText, String);
sqlx_delegate!(VldBool, bool);
impl<S, DB: sqlx::Database> sqlx::Type<DB> for VldFloat<S>
where
f64: sqlx::Type<DB>,
f32: sqlx::Type<DB>,
{
fn type_info() -> DB::TypeInfo {
<f64 as sqlx::Type<DB>>::type_info()
}
fn compatible(ty: &DB::TypeInfo) -> bool {
<f64 as sqlx::Type<DB>>::compatible(ty) || <f32 as sqlx::Type<DB>>::compatible(ty)
}
}
impl<'q, S, DB: sqlx::Database> sqlx::Encode<'q, DB> for VldFloat<S>
where
f64: sqlx::Encode<'q, DB>,
{
fn encode_by_ref(
&self,
buf: &mut SqlxArgBuf<'q, DB>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
self.value.encode_by_ref(buf)
}
}
impl<'r, S, DB: sqlx::Database> sqlx::Decode<'r, DB> for VldFloat<S>
where
f64: sqlx::Decode<'r, DB> + sqlx::Type<DB>,
f32: sqlx::Decode<'r, DB> + sqlx::Type<DB>,
{
fn decode(
value: <DB as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
use sqlx::ValueRef as _;
let ty = value.type_info().into_owned();
let v = if !<f64 as sqlx::Type<DB>>::compatible(&ty)
&& <f32 as sqlx::Type<DB>>::compatible(&ty)
{
let f = <f32 as sqlx::Decode<'r, DB>>::decode(value)?;
f.to_string().parse().unwrap_or(f64::from(f))
} else {
<f64 as sqlx::Decode<'r, DB>>::decode(value)?
};
Ok(Self::new_unchecked(v))
}
}
impl<S, DB: sqlx::Database> sqlx::Type<DB> for VldInt<S>
where
i64: sqlx::Type<DB>,
i32: sqlx::Type<DB>,
i16: sqlx::Type<DB>,
{
fn type_info() -> DB::TypeInfo {
<i64 as sqlx::Type<DB>>::type_info()
}
fn compatible(ty: &DB::TypeInfo) -> bool {
<i64 as sqlx::Type<DB>>::compatible(ty)
|| <i32 as sqlx::Type<DB>>::compatible(ty)
|| <i16 as sqlx::Type<DB>>::compatible(ty)
}
}
impl<'q, S, DB: sqlx::Database> sqlx::Encode<'q, DB> for VldInt<S>
where
i64: sqlx::Encode<'q, DB>,
{
fn encode_by_ref(
&self,
buf: &mut SqlxArgBuf<'q, DB>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
self.value.encode_by_ref(buf)
}
}
impl<'r, S, DB: sqlx::Database> sqlx::Decode<'r, DB> for VldInt<S>
where
i64: sqlx::Decode<'r, DB> + sqlx::Type<DB>,
i32: sqlx::Decode<'r, DB> + sqlx::Type<DB>,
i16: sqlx::Decode<'r, DB> + sqlx::Type<DB>,
{
fn decode(
value: <DB as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
use sqlx::ValueRef as _;
let ty = value.type_info().into_owned();
let v = if <i64 as sqlx::Type<DB>>::compatible(&ty) {
<i64 as sqlx::Decode<'r, DB>>::decode(value)?
} else if <i32 as sqlx::Type<DB>>::compatible(&ty) {
<i32 as sqlx::Decode<'r, DB>>::decode(value)?.into()
} else if <i16 as sqlx::Type<DB>>::compatible(&ty) {
<i16 as sqlx::Decode<'r, DB>>::decode(value)?.into()
} else {
<i64 as sqlx::Decode<'r, DB>>::decode(value)?
};
Ok(Self::new_unchecked(v))
}
}
#[cfg(all(test, feature = "postgres"))]
mod pg_tests {
use super::*;
use sqlx::Type;
use sqlx::postgres::Postgres;
struct AnySchema;
#[test]
fn vld_float_compatible_with_float4() {
for ty in [
<f32 as Type<Postgres>>::type_info(),
<f64 as Type<Postgres>>::type_info(),
] {
assert!(<VldFloat<AnySchema> as Type<Postgres>>::compatible(&ty));
}
}
#[test]
fn vld_int_compatible_with_int4_and_int2() {
for ty in [
<i16 as Type<Postgres>>::type_info(),
<i32 as Type<Postgres>>::type_info(),
<i64 as Type<Postgres>>::type_info(),
] {
assert!(<VldInt<AnySchema> as Type<Postgres>>::compatible(&ty));
}
}
}
pub mod prelude {
#[allow(deprecated)]
pub use crate::{
Validated, VldBool, VldFloat, VldInt, VldSqlxError, VldText, validate_insert, validate_row,
validate_rows, validate_update,
};
pub use vld::prelude::*;
}