1#![warn(missing_docs)]
48
49#[cfg(all(feature = "sqlx-0_8", feature = "sqlx-0_9"))]
50compile_error!("Enable exactly one SQLx version feature: `sqlx-0_8` or `sqlx-0_9`.");
51#[cfg(not(any(feature = "sqlx-0_8", feature = "sqlx-0_9")))]
52compile_error!("Enable one SQLx version feature: `sqlx-0_8` or `sqlx-0_9`.");
53
54#[cfg(feature = "sqlx-0_8")]
55use sqlx08 as sqlx;
56#[cfg(feature = "sqlx-0_9")]
57use sqlx09 as sqlx;
58
59pub use vld;
60
61#[cfg(feature = "sqlx-0_8")]
62type SqlxArgBuf<'q, DB> = <DB as sqlx::Database>::ArgumentBuffer<'q>;
63#[cfg(feature = "sqlx-0_9")]
64type SqlxArgBuf<'q, DB> = <DB as sqlx::Database>::ArgumentBuffer;
65
66vld_integration_common::__vld_db_error!(
69 VldSqlxError
71);
72
73impl From<VldSqlxError> for sqlx::Error {
76 fn from(e: VldSqlxError) -> Self {
77 sqlx::Error::Encode(Box::new(e))
78 }
79}
80
81vld_integration_common::__vld_db_validated!(VldSqlxError);
82
83vld_integration_common::__vld_db_wrappers! {
84 VldSqlxError;
85 text: []
86 int: []
87 float: []
88 bool: []
89}
90
91pub fn validate_insert<S, T>(value: &T) -> Result<(), VldSqlxError>
116where
117 S: vld::schema::VldParse,
118 T: serde::Serialize,
119{
120 __vld_validate::<S, T>(value).map(|_| ())
121}
122
123#[deprecated(
125 since = "0.5.0",
126 note = "identical to `validate_insert`; use it instead"
127)]
128pub fn validate_update<S, T>(value: &T) -> Result<(), VldSqlxError>
129where
130 S: vld::schema::VldParse,
131 T: serde::Serialize,
132{
133 validate_insert::<S, T>(value)
134}
135
136pub fn validate_row<S, T>(value: &T) -> Result<(), VldSqlxError>
139where
140 S: vld::schema::VldParse,
141 T: serde::Serialize,
142{
143 validate_insert::<S, T>(value)
144}
145
146pub fn validate_rows<S, T>(rows: &[T]) -> Result<(), (usize, VldSqlxError)>
150where
151 S: vld::schema::VldParse,
152 T: serde::Serialize,
153{
154 for (i, row) in rows.iter().enumerate() {
155 validate_row::<S, T>(row).map_err(|e| (i, e))?;
156 }
157 Ok(())
158}
159
160macro_rules! sqlx_delegate {
166 ($Name:ident, $inner:ty) => {
167 impl<S, DB: sqlx::Database> sqlx::Type<DB> for $Name<S>
168 where
169 $inner: sqlx::Type<DB>,
170 {
171 fn type_info() -> DB::TypeInfo {
172 <$inner as sqlx::Type<DB>>::type_info()
173 }
174 fn compatible(ty: &DB::TypeInfo) -> bool {
175 <$inner as sqlx::Type<DB>>::compatible(ty)
176 }
177 }
178
179 impl<'q, S, DB: sqlx::Database> sqlx::Encode<'q, DB> for $Name<S>
180 where
181 $inner: sqlx::Encode<'q, DB>,
182 {
183 fn encode_by_ref(
184 &self,
185 buf: &mut SqlxArgBuf<'q, DB>,
186 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
187 self.value.encode_by_ref(buf)
188 }
189 }
190
191 impl<'r, S, DB: sqlx::Database> sqlx::Decode<'r, DB> for $Name<S>
192 where
193 $inner: sqlx::Decode<'r, DB>,
194 {
195 fn decode(
196 value: <DB as sqlx::Database>::ValueRef<'r>,
197 ) -> Result<Self, sqlx::error::BoxDynError> {
198 Ok(Self::new_unchecked(
199 <$inner as sqlx::Decode<'r, DB>>::decode(value)?,
200 ))
201 }
202 }
203 };
204}
205
206sqlx_delegate!(VldText, String);
207sqlx_delegate!(VldBool, bool);
208
209impl<S, DB: sqlx::Database> sqlx::Type<DB> for VldFloat<S>
212where
213 f64: sqlx::Type<DB>,
214 f32: sqlx::Type<DB>,
215{
216 fn type_info() -> DB::TypeInfo {
217 <f64 as sqlx::Type<DB>>::type_info()
218 }
219 fn compatible(ty: &DB::TypeInfo) -> bool {
220 <f64 as sqlx::Type<DB>>::compatible(ty) || <f32 as sqlx::Type<DB>>::compatible(ty)
221 }
222}
223
224impl<'q, S, DB: sqlx::Database> sqlx::Encode<'q, DB> for VldFloat<S>
225where
226 f64: sqlx::Encode<'q, DB>,
227{
228 fn encode_by_ref(
229 &self,
230 buf: &mut SqlxArgBuf<'q, DB>,
231 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
232 self.value.encode_by_ref(buf)
233 }
234}
235
236impl<'r, S, DB: sqlx::Database> sqlx::Decode<'r, DB> for VldFloat<S>
237where
238 f64: sqlx::Decode<'r, DB> + sqlx::Type<DB>,
239 f32: sqlx::Decode<'r, DB> + sqlx::Type<DB>,
240{
241 fn decode(
242 value: <DB as sqlx::Database>::ValueRef<'r>,
243 ) -> Result<Self, sqlx::error::BoxDynError> {
244 use sqlx::ValueRef as _;
245 let ty = value.type_info().into_owned();
246 let v = if !<f64 as sqlx::Type<DB>>::compatible(&ty)
247 && <f32 as sqlx::Type<DB>>::compatible(&ty)
248 {
249 let f = <f32 as sqlx::Decode<'r, DB>>::decode(value)?;
250 f.to_string().parse().unwrap_or(f64::from(f))
251 } else {
252 <f64 as sqlx::Decode<'r, DB>>::decode(value)?
253 };
254 Ok(Self::new_unchecked(v))
255 }
256}
257
258impl<S, DB: sqlx::Database> sqlx::Type<DB> for VldInt<S>
261where
262 i64: sqlx::Type<DB>,
263 i32: sqlx::Type<DB>,
264 i16: sqlx::Type<DB>,
265{
266 fn type_info() -> DB::TypeInfo {
267 <i64 as sqlx::Type<DB>>::type_info()
268 }
269 fn compatible(ty: &DB::TypeInfo) -> bool {
270 <i64 as sqlx::Type<DB>>::compatible(ty)
271 || <i32 as sqlx::Type<DB>>::compatible(ty)
272 || <i16 as sqlx::Type<DB>>::compatible(ty)
273 }
274}
275
276impl<'q, S, DB: sqlx::Database> sqlx::Encode<'q, DB> for VldInt<S>
277where
278 i64: sqlx::Encode<'q, DB>,
279{
280 fn encode_by_ref(
281 &self,
282 buf: &mut SqlxArgBuf<'q, DB>,
283 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
284 self.value.encode_by_ref(buf)
285 }
286}
287
288impl<'r, S, DB: sqlx::Database> sqlx::Decode<'r, DB> for VldInt<S>
289where
290 i64: sqlx::Decode<'r, DB> + sqlx::Type<DB>,
291 i32: sqlx::Decode<'r, DB> + sqlx::Type<DB>,
292 i16: sqlx::Decode<'r, DB> + sqlx::Type<DB>,
293{
294 fn decode(
295 value: <DB as sqlx::Database>::ValueRef<'r>,
296 ) -> Result<Self, sqlx::error::BoxDynError> {
297 use sqlx::ValueRef as _;
298 let ty = value.type_info().into_owned();
299 let v = if <i64 as sqlx::Type<DB>>::compatible(&ty) {
300 <i64 as sqlx::Decode<'r, DB>>::decode(value)?
301 } else if <i32 as sqlx::Type<DB>>::compatible(&ty) {
302 <i32 as sqlx::Decode<'r, DB>>::decode(value)?.into()
303 } else if <i16 as sqlx::Type<DB>>::compatible(&ty) {
304 <i16 as sqlx::Decode<'r, DB>>::decode(value)?.into()
305 } else {
306 <i64 as sqlx::Decode<'r, DB>>::decode(value)?
307 };
308 Ok(Self::new_unchecked(v))
309 }
310}
311
312#[cfg(all(test, feature = "postgres"))]
313mod pg_tests {
314 use super::*;
315 use sqlx::Type;
316 use sqlx::postgres::Postgres;
317
318 struct AnySchema;
319
320 #[test]
321 fn vld_float_compatible_with_float4() {
322 for ty in [
323 <f32 as Type<Postgres>>::type_info(),
324 <f64 as Type<Postgres>>::type_info(),
325 ] {
326 assert!(<VldFloat<AnySchema> as Type<Postgres>>::compatible(&ty));
327 }
328 }
329
330 #[test]
331 fn vld_int_compatible_with_int4_and_int2() {
332 for ty in [
333 <i16 as Type<Postgres>>::type_info(),
334 <i32 as Type<Postgres>>::type_info(),
335 <i64 as Type<Postgres>>::type_info(),
336 ] {
337 assert!(<VldInt<AnySchema> as Type<Postgres>>::compatible(&ty));
338 }
339 }
340}
341
342pub mod prelude {
346 #[allow(deprecated)]
347 pub use crate::{
348 Validated, VldBool, VldFloat, VldInt, VldSqlxError, VldText, validate_insert, validate_row,
349 validate_rows, validate_update,
350 };
351 pub use vld::prelude::*;
352}