use std::fmt;
use std::ops::Deref;
pub use vld;
#[derive(Debug, Clone)]
pub enum VldSqlxError {
Validation(vld::error::VldError),
Serialization(String),
}
impl fmt::Display for VldSqlxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
VldSqlxError::Validation(e) => write!(f, "Validation error: {}", e),
VldSqlxError::Serialization(e) => write!(f, "Serialization error: {}", e),
}
}
}
impl std::error::Error for VldSqlxError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
VldSqlxError::Validation(e) => Some(e),
VldSqlxError::Serialization(_) => None,
}
}
}
impl From<vld::error::VldError> for VldSqlxError {
fn from(e: vld::error::VldError) -> Self {
VldSqlxError::Validation(e)
}
}
impl From<VldSqlxError> for sqlx::Error {
fn from(e: VldSqlxError) -> Self {
sqlx::Error::Protocol(e.to_string())
}
}
pub struct Validated<S, T> {
inner: T,
_schema: std::marker::PhantomData<S>,
}
impl<S, T> Validated<S, T>
where
S: vld::schema::VldParse,
T: serde::Serialize,
{
pub fn new(value: T) -> Result<Self, VldSqlxError> {
let json =
serde_json::to_value(&value).map_err(|e| VldSqlxError::Serialization(e.to_string()))?;
S::vld_parse_value(&json).map_err(VldSqlxError::Validation)?;
Ok(Self {
inner: value,
_schema: std::marker::PhantomData,
})
}
pub fn inner(&self) -> &T {
&self.inner
}
pub fn into_inner(self) -> T {
self.inner
}
}
impl<S, T> Deref for Validated<S, T> {
type Target = T;
fn deref(&self) -> &T {
&self.inner
}
}
impl<S, T: fmt::Debug> fmt::Debug for Validated<S, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Validated")
.field("inner", &self.inner)
.finish()
}
}
impl<S, T: Clone> Clone for Validated<S, T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_schema: std::marker::PhantomData,
}
}
}
pub fn validate_insert<S, T>(value: &T) -> Result<(), VldSqlxError>
where
S: vld::schema::VldParse,
T: serde::Serialize,
{
let json =
serde_json::to_value(value).map_err(|e| VldSqlxError::Serialization(e.to_string()))?;
S::vld_parse_value(&json).map_err(VldSqlxError::Validation)?;
Ok(())
}
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(())
}
pub struct VldText<S> {
value: String,
_schema: std::marker::PhantomData<S>,
}
impl<S> Clone for VldText<S> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
_schema: std::marker::PhantomData,
}
}
}
impl<S> PartialEq for VldText<S> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<S> Eq for VldText<S> {}
impl<S> PartialOrd for VldText<S> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<S> Ord for VldText<S> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.value.cmp(&other.value)
}
}
impl<S> std::hash::Hash for VldText<S> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl<S: vld::schema::VldParse> VldText<S> {
pub fn new(input: impl Into<String>) -> Result<Self, VldSqlxError> {
let s = input.into();
let json = serde_json::json!({ "value": s });
S::vld_parse_value(&json).map_err(VldSqlxError::Validation)?;
Ok(Self {
value: s,
_schema: std::marker::PhantomData,
})
}
pub fn new_unchecked(input: impl Into<String>) -> Self {
Self {
value: input.into(),
_schema: std::marker::PhantomData,
}
}
pub fn as_str(&self) -> &str {
&self.value
}
pub fn into_inner(self) -> String {
self.value
}
}
impl<S> Deref for VldText<S> {
type Target = str;
fn deref(&self) -> &str {
&self.value
}
}
impl<S> AsRef<str> for VldText<S> {
fn as_ref(&self) -> &str {
&self.value
}
}
impl<S> fmt::Debug for VldText<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "VldText({:?})", self.value)
}
}
impl<S> fmt::Display for VldText<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.value)
}
}
impl<S> serde::Serialize for VldText<S> {
fn serialize<Ser: serde::Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
self.value.serialize(serializer)
}
}
impl<'de, S: vld::schema::VldParse> serde::Deserialize<'de> for VldText<S> {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
VldText::<S>::new(&s).map_err(serde::de::Error::custom)
}
}
impl<S, DB: sqlx::Database> sqlx::Type<DB> for VldText<S>
where
String: sqlx::Type<DB>,
{
fn type_info() -> DB::TypeInfo {
<String as sqlx::Type<DB>>::type_info()
}
fn compatible(ty: &DB::TypeInfo) -> bool {
<String as sqlx::Type<DB>>::compatible(ty)
}
}
impl<'q, S, DB: sqlx::Database> sqlx::Encode<'q, DB> for VldText<S>
where
String: sqlx::Encode<'q, DB>,
{
fn encode_by_ref(
&self,
buf: &mut <DB as sqlx::Database>::ArgumentBuffer<'q>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
self.value.encode_by_ref(buf)
}
}
impl<'r, S: vld::schema::VldParse, DB: sqlx::Database> sqlx::Decode<'r, DB> for VldText<S>
where
String: sqlx::Decode<'r, DB>,
{
fn decode(
value: <DB as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
let s = <String as sqlx::Decode<'r, DB>>::decode(value)?;
Ok(Self::new_unchecked(s))
}
}
pub struct VldInt<S> {
value: i64,
_schema: std::marker::PhantomData<S>,
}
impl<S> Clone for VldInt<S> {
fn clone(&self) -> Self {
*self
}
}
impl<S> Copy for VldInt<S> {}
impl<S> PartialEq for VldInt<S> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<S> Eq for VldInt<S> {}
impl<S> PartialOrd for VldInt<S> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<S> Ord for VldInt<S> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.value.cmp(&other.value)
}
}
impl<S> std::hash::Hash for VldInt<S> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl<S: vld::schema::VldParse> VldInt<S> {
pub fn new(input: i64) -> Result<Self, VldSqlxError> {
let json = serde_json::json!({ "value": input });
S::vld_parse_value(&json).map_err(VldSqlxError::Validation)?;
Ok(Self {
value: input,
_schema: std::marker::PhantomData,
})
}
pub fn new_unchecked(input: i64) -> Self {
Self {
value: input,
_schema: std::marker::PhantomData,
}
}
pub fn get(&self) -> i64 {
self.value
}
}
impl<S> Deref for VldInt<S> {
type Target = i64;
fn deref(&self) -> &i64 {
&self.value
}
}
impl<S> fmt::Debug for VldInt<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "VldInt({})", self.value)
}
}
impl<S> fmt::Display for VldInt<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl<S> serde::Serialize for VldInt<S> {
fn serialize<Ser: serde::Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
self.value.serialize(serializer)
}
}
impl<'de, S: vld::schema::VldParse> serde::Deserialize<'de> for VldInt<S> {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let v = i64::deserialize(deserializer)?;
VldInt::<S>::new(v).map_err(serde::de::Error::custom)
}
}
impl<S, DB: sqlx::Database> sqlx::Type<DB> for VldInt<S>
where
i64: 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)
}
}
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 <DB as sqlx::Database>::ArgumentBuffer<'q>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
self.value.encode_by_ref(buf)
}
}
impl<'r, S: vld::schema::VldParse, DB: sqlx::Database> sqlx::Decode<'r, DB> for VldInt<S>
where
i64: sqlx::Decode<'r, DB>,
{
fn decode(
value: <DB as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
let v = <i64 as sqlx::Decode<'r, DB>>::decode(value)?;
Ok(Self::new_unchecked(v))
}
}
pub struct VldBool<S> {
value: bool,
_schema: std::marker::PhantomData<S>,
}
impl<S> Clone for VldBool<S> {
fn clone(&self) -> Self {
*self
}
}
impl<S> Copy for VldBool<S> {}
impl<S> PartialEq for VldBool<S> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<S> Eq for VldBool<S> {}
impl<S: vld::schema::VldParse> VldBool<S> {
pub fn new(input: bool) -> Result<Self, VldSqlxError> {
let json = serde_json::json!({ "value": input });
S::vld_parse_value(&json).map_err(VldSqlxError::Validation)?;
Ok(Self {
value: input,
_schema: std::marker::PhantomData,
})
}
pub fn new_unchecked(input: bool) -> Self {
Self {
value: input,
_schema: std::marker::PhantomData,
}
}
pub fn get(&self) -> bool {
self.value
}
}
impl<S> Deref for VldBool<S> {
type Target = bool;
fn deref(&self) -> &bool {
&self.value
}
}
impl<S> fmt::Debug for VldBool<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "VldBool({})", self.value)
}
}
impl<S> fmt::Display for VldBool<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl<S> serde::Serialize for VldBool<S> {
fn serialize<Ser: serde::Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
self.value.serialize(serializer)
}
}
impl<'de, S: vld::schema::VldParse> serde::Deserialize<'de> for VldBool<S> {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let v = bool::deserialize(deserializer)?;
VldBool::<S>::new(v).map_err(serde::de::Error::custom)
}
}
impl<S, DB: sqlx::Database> sqlx::Type<DB> for VldBool<S>
where
bool: sqlx::Type<DB>,
{
fn type_info() -> DB::TypeInfo {
<bool as sqlx::Type<DB>>::type_info()
}
fn compatible(ty: &DB::TypeInfo) -> bool {
<bool as sqlx::Type<DB>>::compatible(ty)
}
}
impl<'q, S, DB: sqlx::Database> sqlx::Encode<'q, DB> for VldBool<S>
where
bool: sqlx::Encode<'q, DB>,
{
fn encode_by_ref(
&self,
buf: &mut <DB as sqlx::Database>::ArgumentBuffer<'q>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
self.value.encode_by_ref(buf)
}
}
impl<'r, S: vld::schema::VldParse, DB: sqlx::Database> sqlx::Decode<'r, DB> for VldBool<S>
where
bool: sqlx::Decode<'r, DB>,
{
fn decode(
value: <DB as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
let v = <bool as sqlx::Decode<'r, DB>>::decode(value)?;
Ok(Self::new_unchecked(v))
}
}
pub struct VldFloat<S> {
value: f64,
_schema: std::marker::PhantomData<S>,
}
impl<S> Clone for VldFloat<S> {
fn clone(&self) -> Self {
*self
}
}
impl<S> Copy for VldFloat<S> {}
impl<S> PartialEq for VldFloat<S> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<S: vld::schema::VldParse> VldFloat<S> {
pub fn new(input: f64) -> Result<Self, VldSqlxError> {
let json = serde_json::json!({ "value": input });
S::vld_parse_value(&json).map_err(VldSqlxError::Validation)?;
Ok(Self {
value: input,
_schema: std::marker::PhantomData,
})
}
pub fn new_unchecked(input: f64) -> Self {
Self {
value: input,
_schema: std::marker::PhantomData,
}
}
pub fn get(&self) -> f64 {
self.value
}
}
impl<S> Deref for VldFloat<S> {
type Target = f64;
fn deref(&self) -> &f64 {
&self.value
}
}
impl<S> fmt::Debug for VldFloat<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "VldFloat({})", self.value)
}
}
impl<S> fmt::Display for VldFloat<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl<S> serde::Serialize for VldFloat<S> {
fn serialize<Ser: serde::Serializer>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> {
self.value.serialize(serializer)
}
}
impl<'de, S: vld::schema::VldParse> serde::Deserialize<'de> for VldFloat<S> {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let v = f64::deserialize(deserializer)?;
VldFloat::<S>::new(v).map_err(serde::de::Error::custom)
}
}
impl<S, DB: sqlx::Database> sqlx::Type<DB> for VldFloat<S>
where
f64: 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)
}
}
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 <DB as sqlx::Database>::ArgumentBuffer<'q>,
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
self.value.encode_by_ref(buf)
}
}
impl<'r, S: vld::schema::VldParse, DB: sqlx::Database> sqlx::Decode<'r, DB> for VldFloat<S>
where
f64: sqlx::Decode<'r, DB>,
{
fn decode(
value: <DB as sqlx::Database>::ValueRef<'r>,
) -> Result<Self, sqlx::error::BoxDynError> {
let v = <f64 as sqlx::Decode<'r, DB>>::decode(value)?;
Ok(Self::new_unchecked(v))
}
}
pub mod prelude {
pub use crate::{
validate_insert, validate_row, validate_rows, validate_update, Validated, VldBool,
VldFloat, VldInt, VldSqlxError, VldText,
};
pub use vld::prelude::*;
}