serde_valid/lib.rs
1//! # Serde Valid
2//!
3//! [](https://crates.io/crates/serde_valid)
4//! [](https://docs.rs/serde_valid/latest/serde_valid/)
5//! [](https://github.com/Naereen/StrapDown.js/blob/master/LICENSE)
6//!
7//! This is [JSON Schema](https://json-schema.org/) based validation tool using [serde](https://github.com/serde-rs/serde).
8//!
9//! ## Usage
10//!
11//! You derive `Validate` trait, and write validations.
12//!
13//! ```rust
14//! use serde_valid::Validate;
15//!
16//! #[derive(Validate)]
17//! struct Data {
18//! #[validate(minimum = 0)]
19//! #[validate(maximum = 10)]
20//! val: i32,
21//! }
22//!
23//! #[derive(Validate)]
24//! enum DataEnum {
25//! Named {
26//! #[validate]
27//! a: Data,
28//! },
29//! }
30//!
31//! let s = DataEnum::Named {
32//! a: Data { val: 5 },
33//! };
34//!
35//! assert!(s.validate().is_ok());
36//! ```
37//!
38//! ## Feature Flags
39//!
40//! - `toml` - provide serialization/deserialization in `toml` format.
41//! - `yaml` - provide serialization/deserialization in `yaml` format.
42//! - `i128` - support `i128`/`u128` type (default).
43//! - `fluent` - provide localization using [fluent](https://projectfluent.org/).
44//!
45//! ## Validations
46//!
47//! Serde Valid support standard validation based JSON Schema.
48//!
49//! | Type | Serde Valid (validate derive) | Serde Valid (validate trait) | JSON Schema |
50//! | :-----: | :------------------------------------- | :--------------------------- | :-------------------------------------------------------------------------------------------- |
51//! | String | `#[validate(max_length = 5)]` | [`ValidateMaxLength`] | [maxLength](https://json-schema.org/understanding-json-schema/reference/string#length) |
52//! | String | `#[validate(min_length = 5)]` | [`ValidateMinLength`] | [minLength](https://json-schema.org/understanding-json-schema/reference/string#length) |
53//! | String | `#[validate(pattern = r"^\d{5}$")]` | [`ValidatePattern`] | [pattern](https://json-schema.org/understanding-json-schema/reference/string#regexp) |
54//! | Numeric | `#[validate(maximum = 5)]` | [`ValidateMaximum`] | [maximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) |
55//! | Numeric | `#[validate(minimum = 5)]` | [`ValidateMinimum`] | [minimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) |
56//! | Numeric | `#[validate(exclusive_maximum = 5)]` | [`ValidateExclusiveMaximum`] | [exclusiveMaximum](https://json-schema.org/understanding-json-schema/reference/numeric#range) |
57//! | Numeric | `#[validate(exclusive_minimum = 5)]` | [`ValidateExclusiveMinimum`] | [exclusiveMinimum](https://json-schema.org/understanding-json-schema/reference/numeric#range) |
58//! | Numeric | `#[validate(multiple_of = 5)]` | [`ValidateMultipleOf`] | [multipleOf](https://json-schema.org/understanding-json-schema/reference/numeric#multiples) |
59//! | Object | `#[validate(max_properties = 5)]` | [`ValidateMaxProperties`] | [maxProperties](https://json-schema.org/understanding-json-schema/reference/object#size) |
60//! | Object | `#[validate(min_properties = 5)]` | [`ValidateMinProperties`] | [minProperties](https://json-schema.org/understanding-json-schema/reference/object#size) |
61//! | Array | `#[validate(max_items = 5)]` | [`ValidateMaxItems`] | [maxItems](https://json-schema.org/understanding-json-schema/reference/array#length) |
62//! | Array | `#[validate(min_items = 5)]` | [`ValidateMinItems`] | [minItems](https://json-schema.org/understanding-json-schema/reference/array#length) |
63//! | Array | `#[validate(unique_items)]` | [`ValidateUniqueItems`] | [uniqueItems](https://json-schema.org/understanding-json-schema/reference/array#uniqueItems) |
64//! | Generic | `#[validate(enumerate = [5, 10, 15])]` | [`ValidateEnumerate`] | [enum](https://json-schema.org/understanding-json-schema/reference/enum) |
65//!
66//! In addition, [serde_valid::utils][module@crate::utils] provides a type of validation not described in the JSON schema specification.
67//!
68//! | Type | Serde Valid (validate derive) | Serde Valid (validation function) |
69//! | :------------------------------------------------------------------: | :--------------------------------------------------------- | :----------------------------------------------------------------------- |
70//! | [Duration](https://doc.rust-lang.org/core/time/struct.Duration.html) | `#[validate(custom = duration_maximum(SECOND))]` | [duration_maximum][`crate::utils::duration_maximum`] |
71//! | [Duration](https://doc.rust-lang.org/core/time/struct.Duration.html) | `#[validate(custom = duration_minimum(ZERO))]` | [duration_minimum][`crate::utils::duration_minimum`] |
72//! | [Duration](https://doc.rust-lang.org/core/time/struct.Duration.html) | `#[validate(custom = duration_exclusive_maximum(SECOND))]` | [duration_exclusive_maximum][`crate::utils::duration_exclusive_maximum`] |
73//! | [Duration](https://doc.rust-lang.org/core/time/struct.Duration.html) | `#[validate(custom = duration_exclusive_minimum(ZERO))]` | [duration_exclusive_minimum][`crate::utils::duration_exclusive_minimum`] |
74//!
75//! ## Complete Constructor (Deserialization)
76//!
77//! Serde Valid support complete constructor method using by
78//! [`serde_valid::json::FromJsonValue`](json::FromJsonValue) trait.
79//!
80//! ```rust
81//! use serde::Deserialize;
82//! use serde_valid::Validate;
83//! use serde_valid::json::{json, FromJsonValue};
84//!
85//! #[derive(Debug, Deserialize, Validate)]
86//! struct Data {
87//! #[validate(maximum = 100)]
88//! val: i32,
89//! }
90//!
91//! // Deserialization and Validation!! ๐
92//! let err = Data::from_json_value(json!({ "val": 123 })).unwrap_err();
93//!
94//! assert_eq!(
95//! err.to_string(),
96//! json!({
97//! "errors": [],
98//! "properties": {
99//! "val": {
100//! "errors": ["The number must be `<= 100`."]
101//! }
102//! }
103//! })
104//! .to_string()
105//! );
106//! ```
107//!
108//! You can force validation by only deserialization through `serde_valid`, and removing
109//! `serde_json` from `Cargo.toml` of your project.
110//!
111//! ## Serialization
112//!
113//! For serialization, provides [`serde_valid::json::ToJsonString`](json::ToJsonString) trait.
114//!
115//! ```rust
116//! use serde::Serialize;
117//! use serde_valid::Validate;
118//! use serde_valid::json::{json, ToJsonString};
119//!
120//! #[derive(Debug, Serialize, Validate)]
121//! struct Data {
122//! #[validate(maximum = 100)]
123//! val: i32,
124//! }
125//!
126//! assert_eq!(
127//! Data{ val: 12i32 }.to_json_string().unwrap(),
128//! json!({ "val": 12i32 }).to_json_string().unwrap()
129//! );
130//! ```
131//!
132//! ## Custom Message
133//!
134//! For user custom message, Serde Valid provides `message_fn` or `message`.
135//!
136//! ```rust
137//! use serde_json::json;
138//! use serde_valid::Validate;
139//!
140//! #[inline]
141//! fn min_error_message(_params: &serde_valid::MinItemsError) -> String {
142//! "this is custom message_fn.".to_string()
143//! }
144//!
145//! #[derive(Validate)]
146//! struct Data {
147//! #[validate(min_items = 4, message_fn = min_error_message)]
148//! #[validate(max_items = 2, message = "this is custom message.")]
149//! val: Vec<i32>,
150//! }
151//!
152//! let s = Data { val: vec![1, 2, 3] };
153//!
154//! assert_eq!(
155//! s.validate().unwrap_err().to_string(),
156//! json!({
157//! "errors": [],
158//! "properties": {
159//! "val": {
160//! "errors": [
161//! "this is custom message_fn.",
162//! "this is custom message."
163//! ]
164//! }
165//! }
166//! })
167//! .to_string()
168//! );
169//! ```
170//!
171//! ### Fluent localization
172//!
173//! <section class="warning">
174//! <code>fluent</code> feature is required.
175//! </section>
176//!
177//! You can also use [fluent](https://projectfluent.org/) localization by using `fluent` feature.
178//!
179//! Allow the following attributes:
180//! - `#[validate(..., fluent("message-id", key1 = value1, ...))]`
181//! - `#[validate(..., message_l10n = fluent("message-id", key1 = value1, ...))]`
182//!
183//! ```rust
184//! # #[cfg(feature = "fluent")] {
185//! # use fluent::{FluentBundle, FluentResource};
186//! use unic_langid::LanguageIdentifier;
187//! use serde_json::json;
188//! use serde_valid::{fluent::Localize, Validate};
189//!
190//! # fn get_bundle(source: impl Into<String>) -> FluentBundle<FluentResource> {
191//! # let res = FluentResource::try_new(source.into()).expect("Failed to parse an FTL string.");
192//! # let langid_en: LanguageIdentifier = "ja_JP".parse().expect("Parsing failed");
193//! # let mut bundle = FluentBundle::new(vec![langid_en]);
194//! # bundle.add_resource(res).unwrap();
195//! # bundle
196//! # }
197//!
198//! #[derive(Validate)]
199//! struct Data (
200//! #[validate(min_length = 3, fluent("name-min-length", min_length = 3))]
201//! String,
202//! );
203//!
204//! assert_eq!(
205//! Data("็ฐไธญ".to_string()).validate()
206//! .unwrap_err()
207//! .localize(&get_bundle("name-min-length = ๅๅใฎ้ทใใฏ { $min_length } ๆๅญไปฅไธใงใชใใจใใใพใใใ"))
208//! .to_string(),
209//! json!({
210//! "errors": ["ๅๅใฎ้ทใใฏ \u{2068}3\u{2069} ๆๅญไปฅไธใงใชใใจใใใพใใใ"]
211//! })
212//! .to_string()
213//! );
214//! # }
215//! ```
216//!
217//! ## Custom Validation
218//! ### Single Error Validation
219//! You can use your custom validation using by `#[validate(custom = ...)]`.
220//!
221//! ```rust
222//! use serde_valid::Validate;
223//!
224//! fn user_validation(_val: &i32) -> Result<(), serde_valid::validation::Error> {
225//! Ok(())
226//! }
227//!
228//! #[derive(Validate)]
229//! struct Data {
230//! #[validate(custom = user_validation)]
231//! val: i32,
232//! }
233//!
234//! let s = Data { val: 1 };
235//!
236//! assert!(s.validate().is_ok());
237//! ```
238//!
239//! And you can also use closure.
240//!
241//! ```rust
242//! use serde_valid::Validate;
243//!
244//! fn user_validation(_val: &i32, param1: bool) -> Result<(), serde_valid::validation::Error> {
245//! Ok(())
246//! }
247//!
248//! #[derive(Validate)]
249//! struct Data {
250//! #[validate(custom = |v| user_validation(v, true))]
251//! val: i32,
252//! }
253//!
254//! let s = Data { val: 1 };
255//!
256//! assert!(s.validate().is_ok());
257//! ```
258//!
259//! You can use **closure**, so you can access other fields of the struct by using the `self` keyword.
260//!
261//! ```rust
262//! use serde_valid::Validate;
263//!
264//! #[derive(Validate)]
265//! struct Data {
266//! val1: i32,
267//! #[validate(custom = |val2: &i32| {
268//! if self.val1 < *val2 {
269//! Ok(())
270//! } else {
271//! Err(serde_valid::validation::Error::Custom("val2 must be greater than val1".to_owned()))
272//! }
273//! })]
274//! val2: i32,
275//! }
276//!
277//! let s = Data { val1: 2, val2: 1 };
278//!
279//! assert_eq!(
280//! s.validate().unwrap_err().to_string(),
281//! serde_json::json!({
282//! "errors": [],
283//! "properties": {
284//! "val2": {
285//! "errors": ["val2 must be greater than val1"]
286//! }
287//! }
288//! })
289//! .to_string()
290//! );
291//! ```
292//!
293//! Custom validation is suitable for handling convenience validations not defined in JSON Schema.
294//! `serde_valid::utils::*` provides convenience functions for specific types.
295//!
296//! ```rust
297//! use serde_json::json;
298//! use serde_valid::Validate;
299//! use serde_valid::utils::{duration_maximum, duration_minimum};
300//!
301//!
302//! #[derive(Validate)]
303//! struct Data {
304//! #[validate(custom = duration_maximum(std::time::Duration::from_micros(5)))]
305//! #[validate(custom = duration_minimum(std::time::Duration::from_micros(0)))]
306//! val1: std::time::Duration,
307//! }
308//!
309//! let s = Data {
310//! val1: std::time::Duration::from_micros(1),
311//! };
312//!
313//! assert!(s.validate().is_ok());
314//! ```
315//!
316//! ### Multi Errors Validation
317//! If you want to return multiple errors in the use custom validation method, you can use `#[validate(custom = ...)]` same as single error.
318//!
319//! ```rust
320//! use serde_valid::Validate;
321//!
322//! // ๐ Just change the return type from `Result<(), Error>` to `Result<(), Vec<Error>>` !!
323//! fn user_validation(_val: &i32) -> Result<(), Vec<serde_valid::validation::Error>> {
324//! Ok(())
325//! }
326//!
327//! #[derive(Validate)]
328//! struct Data {
329//! #[validate(custom = user_validation)]
330//! val: i32,
331//! }
332//!
333//! let s = Data { val: 1 };
334//!
335//! assert!(s.validate().is_ok());
336//! ```
337//!
338//! ### Multi Fields Validation
339//! Now, you can use `#[validate(custom = ...)]` for multi fields validation.
340//!
341//! ```rust
342//! use serde_json::json;
343//! use serde_valid::Validate;
344//!
345//! fn sample_validation(val1: i32, val2: &str) -> Result<(), serde_valid::validation::Error> {
346//! Ok(())
347//! }
348//!
349//! #[derive(Validate)]
350//! #[validate(custom = |s| sample_validation(s.val1, &s.val2))]
351//! struct Data {
352//! val1: i32,
353//! val2: String,
354//! }
355//!
356//! let s = Data {
357//! val1: 1,
358//! val2: "val2".to_owned(),
359//! };
360//!
361//! assert!(s.validate().is_ok());
362//! ```
363//!
364//! ## Validate Traits
365//!
366//! By implementing the validation trait, Your original type can uses Serde Valid validations.
367//!
368//! ```rust
369//! use serde_valid::Validate;
370//!
371//! struct MyType(String);
372//!
373//! impl serde_valid::ValidateMaxLength for MyType {
374//! fn validate_max_length(&self, max_length: usize) -> Result<(), serde_valid::MaxLengthError> {
375//! self.0.validate_max_length(max_length)
376//! }
377//! }
378//!
379//! #[derive(Validate)]
380//! struct Data {
381//! #[validate(max_length = 5)]
382//! val: MyType,
383//! }
384//!
385//! let s = Data {
386//! val: MyType(String::from("๐๐บ๐๐ฝ๐จโ๐ค๐จโ๐ฉโ๐งโ๐ฆ")),
387//! };
388//!
389//! assert!(s.validate().is_ok());
390//! ```
391//!
392//! ## Validation Errors Format
393//! ### Named Struct
394//! Field errors are output to `properties`.
395//!
396//! ```rust
397//! use serde_json::json;
398//! use serde_valid::Validate;
399//!
400//! #[derive(Validate)]
401//! struct Data {
402//! #[validate(maximum = 4)]
403//! val: u32,
404//! }
405//!
406//! let s = Data { val: 5 };
407//!
408//! assert_eq!(
409//! s.validate().unwrap_err().to_string(),
410//! json!({
411//! "errors": [],
412//! "properties": {
413//! "val": {
414//! "errors": ["The number must be `<= 4`."]
415//! }
416//! }
417//! })
418//! .to_string()
419//! );
420//! ```
421//!
422//! ### Unnamed Struct
423//! Field errors are output to `items`. The key for `items` is guaranteed to be a string of positive
424//! numbers.
425//!
426//! ```rust
427//! use serde_json::json;
428//! use serde_valid::Validate;
429//!
430//! #[derive(Validate)]
431//! struct Data (
432//! #[validate(maximum = 4)] u32,
433//! #[validate(maximum = 3)] u32,
434//! );
435//!
436//! let s = Data ( 5, 4 );
437//!
438//! assert_eq!(
439//! s.validate().unwrap_err().to_string(),
440//! json!({
441//! "errors": [],
442//! "items": {
443//! "0": {
444//! "errors": ["The number must be `<= 4`."]
445//! },
446//! "1": {
447//! "errors": ["The number must be `<= 3`."]
448//! }
449//! }
450//! })
451//! .to_string()
452//! );
453//! ```
454//!
455//! ### New Type
456//! Field errors are output to `errors`.
457//!
458//! ```rust
459//! use serde_json::json;
460//! use serde_valid::Validate;
461//!
462//! #[derive(Validate)]
463//! struct Data (
464//! #[validate(maximum = 4)] u32
465//! );
466//!
467//! let s = Data (5);
468//!
469//! assert_eq!(
470//! s.validate().unwrap_err().to_string(),
471//! json!({
472//! "errors": ["The number must be `<= 4`."]
473//! })
474//! .to_string()
475//! );
476//! ```
477//!
478//! ### Named Enum
479//! Variant errors are output to `properties`.
480//!
481//! ```rust
482//! use serde_json::json;
483//! use serde_valid::Validate;
484//!
485//! #[derive(Validate)]
486//! enum Data {
487//! Named {
488//! #[validate(maximum = 5)]
489//! a: i32,
490//! #[validate(maximum = 5)]
491//! b: i32,
492//! },
493//! }
494//!
495//! let s = Data::Named { a: 6, b: 6 };
496//!
497//! assert_eq!(
498//! s.validate().unwrap_err().to_string(),
499//! json!({
500//! "errors": [],
501//! "properties": {
502//! "a": {
503//! "errors": ["The number must be `<= 5`."]
504//! },
505//! "b": {
506//! "errors": ["The number must be `<= 5`."]
507//! }
508//! }
509//! })
510//! .to_string()
511//! );
512//! ```
513//!
514//! ### Unnamed Enum
515//! Variant errors are output to `items`. The key for `items` is guaranteed to be a string of
516//! positive numbers.
517//!
518//! ```rust
519//! use serde_json::json;
520//! use serde_valid::Validate;
521//!
522//! #[derive(Validate)]
523//! enum Data {
524//! Unnamed (
525//! #[validate(maximum = 5)] i32,
526//! #[validate(maximum = 5)] i32,
527//! ),
528//! }
529//!
530//! let s = Data::Unnamed ( 6, 6 );
531//!
532//! assert_eq!(
533//! s.validate().unwrap_err().to_string(),
534//! json!({
535//! "errors": [],
536//! "items": {
537//! "0": {
538//! "errors": ["The number must be `<= 5`."]
539//! },
540//! "1": {
541//! "errors": ["The number must be `<= 5`."]
542//! }
543//! }
544//! })
545//! .to_string()
546//! );
547//! ```
548//!
549//! ### New Type Enum
550//! Variant errors are output to `errors`.
551//!
552//! ```rust
553//! use serde_json::json;
554//! use serde_valid::Validate;
555//!
556//! #[derive(Validate)]
557//! enum Data {
558//! NewType (
559//! #[validate(maximum = 5)] i32,
560//! ),
561//! }
562//!
563//! let s = Data::NewType ( 6 );
564//!
565//! assert_eq!(
566//! s.validate().unwrap_err().to_string(),
567//! json!({
568//! "errors": ["The number must be `<= 5`."]
569//! })
570//! .to_string()
571//! );
572//! ```
573
574pub mod error;
575mod features;
576pub mod json;
577mod traits;
578pub mod utils;
579pub mod validation;
580
581pub use error::{
582 EnumerateError, Error, ExclusiveMaximumError, ExclusiveMinimumError, MaxItemsError,
583 MaxLengthError, MaxPropertiesError, MaximumError, MinItemsError, MinLengthError,
584 MinPropertiesError, MinimumError, MultipleOfError, PatternError, UniqueItemsError,
585};
586#[allow(unused_imports)]
587pub use features::*;
588use indexmap::IndexMap;
589use std::{borrow::Cow, collections::HashMap};
590pub use validation::{
591 ValidateEnumerate, ValidateExclusiveMaximum, ValidateExclusiveMinimum, ValidateMaxItems,
592 ValidateMaxLength, ValidateMaxProperties, ValidateMaximum, ValidateMinItems, ValidateMinLength,
593 ValidateMinProperties, ValidateMinimum, ValidateMultipleOf, ValidatePattern,
594 ValidateUniqueItems,
595};
596
597pub mod export {
598 #[cfg(feature = "fluent")]
599 pub use fluent;
600 pub use once_cell;
601 pub use regex;
602}
603
604pub trait Validate {
605 fn validate(&self) -> std::result::Result<(), self::validation::Errors>;
606}
607
608impl<T> Validate for Vec<T>
609where
610 T: Validate,
611{
612 fn validate(&self) -> std::result::Result<(), self::validation::Errors> {
613 let mut items = IndexMap::new();
614
615 for (index, item) in self.iter().enumerate() {
616 if let Err(errors) = item.validate() {
617 items.insert(index, errors);
618 }
619 }
620
621 if items.is_empty() {
622 Ok(())
623 } else {
624 Err(self::validation::Errors::Array(
625 validation::error::ArrayErrors::new(vec![], items),
626 ))
627 }
628 }
629}
630
631impl<T, const N: usize> Validate for [T; N]
632where
633 T: Validate,
634{
635 fn validate(&self) -> std::result::Result<(), self::validation::Errors> {
636 let mut items = IndexMap::new();
637
638 for (index, item) in self.iter().enumerate() {
639 if let Err(errors) = item.validate() {
640 items.insert(index, errors);
641 }
642 }
643
644 if items.is_empty() {
645 Ok(())
646 } else {
647 Err(self::validation::Errors::Array(
648 validation::error::ArrayErrors::new(vec![], items),
649 ))
650 }
651 }
652}
653
654impl<K, V> Validate for HashMap<K, V>
655where
656 V: Validate,
657 for<'a> &'a K: Into<String>,
658{
659 fn validate(&self) -> std::result::Result<(), self::validation::Errors> {
660 let mut items = IndexMap::new();
661
662 for (key, value) in self.iter() {
663 if let Err(errors) = value.validate() {
664 items.insert(Cow::from(key.into()), errors);
665 }
666 }
667
668 if items.is_empty() {
669 Ok(())
670 } else {
671 Err(self::validation::Errors::Object(
672 validation::error::ObjectErrors::new(vec![], items),
673 ))
674 }
675 }
676}
677
678impl<K, V> Validate for IndexMap<K, V>
679where
680 V: Validate,
681 for<'a> &'a K: Into<String>,
682{
683 fn validate(&self) -> std::result::Result<(), self::validation::Errors> {
684 let mut items = IndexMap::new();
685
686 for (key, value) in self.iter() {
687 if let Err(errors) = value.validate() {
688 items.insert(Cow::from(key.into()), errors);
689 }
690 }
691
692 if items.is_empty() {
693 Ok(())
694 } else {
695 Err(self::validation::Errors::Object(
696 validation::ObjectErrors::new(vec![], items),
697 ))
698 }
699 }
700}
701
702impl<T> Validate for Option<T>
703where
704 T: Validate,
705{
706 fn validate(&self) -> std::result::Result<(), self::validation::Errors> {
707 match self {
708 Some(value) => value.validate(),
709 None => Ok(()),
710 }
711 }
712}
713
714pub use serde_valid_derive::Validate;
715
716#[cfg(test)]
717pub mod tests {
718 pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
719}