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