Crate serde_valid

Source
Expand description

§Serde Valid

Latest Version crate docs GitHub license

This is JSON Schema based validation tool using serde.

§Usage

You derive Validate trait, and write validations.

use serde_valid::Validate;

#[derive(Validate)]
struct Data {
    #[validate(minimum = 0)]
    #[validate(maximum = 10)]
    val: i32,
}

#[derive(Validate)]
enum DataEnum {
    Named {
        #[validate]
        a: Data,
    },
}

let s = DataEnum::Named {
    a: Data { val: 5 },
};

assert!(s.validate().is_ok());

§Feature Flags

  • toml - provide serialization/deserialization in toml format.
  • yaml - provide serialization/deserialization in yaml format.
  • i128 - support i128/u128 type (default).
  • fluent - provide localization using fluent.

§Validations

Serde Valid support standard validation based JSON Schema.

TypeSerde Valid (validate derive)Serde Valid (validate trait)JSON Schema
String#[validate(max_length = 5)]ValidateMaxLengthmaxLength
String#[validate(min_length = 5)]ValidateMinLengthminLength
String#[validate(pattern = r"^\d{5}$")]ValidatePatternpattern
Numeric#[validate(maximum = 5)]ValidateMaximummaximum
Numeric#[validate(minimum = 5)]ValidateMinimumminimum
Numeric#[validate(exclusive_maximum = 5)]ValidateExclusiveMaximumexclusiveMaximum
Numeric#[validate(exclusive_minimum = 5)]ValidateExclusiveMinimumexclusiveMinimum
Numeric#[validate(multiple_of = 5)]ValidateMultipleOfmultipleOf
Object#[validate(max_properties = 5)]ValidateMaxPropertiesmaxProperties
Object#[validate(min_properties = 5)]ValidateMinPropertiesminProperties
Array#[validate(max_items = 5)]ValidateMaxItemsmaxItems
Array#[validate(min_items = 5)]ValidateMinItemsminItems
Array#[validate(unique_items)]ValidateUniqueItemsuniqueItems
Generic#[validate(enumerate = [5, 10, 15])]ValidateEnumerateenum

In addition, serde_valid::utils provides a type of validation not described in the JSON schema specification.

TypeSerde Valid (validate derive)Serde Valid (validation function)
Duration#[validate(custom = duration_maximum(SECOND))]duration_maximum
Duration#[validate(custom = duration_minimum(ZERO))]duration_minimum
Duration#[validate(custom = duration_exclusive_maximum(SECOND))]duration_exclusive_maximum
Duration#[validate(custom = duration_exclusive_minimum(ZERO))]duration_exclusive_minimum

§Complete Constructor (Deserialization)

Serde Valid support complete constructor method using by serde_valid::json::FromJsonValue trait.

use serde::Deserialize;
use serde_valid::Validate;
use serde_valid::json::{json, FromJsonValue};

#[derive(Debug, Deserialize, Validate)]
struct Data {
    #[validate(maximum = 100)]
    val: i32,
}

// Deserialization and Validation!! 🚀
let err = Data::from_json_value(json!({ "val": 123 })).unwrap_err();

assert_eq!(
    err.to_string(),
    json!({
        "errors": [],
        "properties": {
            "val": {
                "errors": ["The number must be `<= 100`."]
            }
        }
    })
    .to_string()
);

You can force validation by only deserialization through serde_valid, and removing serde_json from Cargo.toml of your project.

§Serialization

For serialization, provides serde_valid::json::ToJsonString trait.

use serde::Serialize;
use serde_valid::Validate;
use serde_valid::json::{json, ToJsonString};

#[derive(Debug, Serialize, Validate)]
struct Data {
    #[validate(maximum = 100)]
    val: i32,
}

assert_eq!(
    Data{ val: 12i32 }.to_json_string().unwrap(),
    json!({ "val": 12i32 }).to_json_string().unwrap()
);

§Custom Message

For user custom message, Serde Valid provides message_fn or message.

use serde_json::json;
use serde_valid::Validate;

#[inline]
fn min_error_message(_params: &serde_valid::MinItemsError) -> String {
    "this is custom message_fn.".to_string()
}

#[derive(Validate)]
struct Data {
    #[validate(min_items = 4, message_fn = min_error_message)]
    #[validate(max_items = 2, message = "this is custom message.")]
    val: Vec<i32>,
}

let s = Data { val: vec![1, 2, 3] };

assert_eq!(
    s.validate().unwrap_err().to_string(),
    json!({
        "errors": [],
        "properties": {
            "val": {
                "errors": [
                    "this is custom message_fn.",
                    "this is custom message."
                ]
            }
        }
    })
    .to_string()
);

§Fluent localization

fluent feature is required.

You can also use fluent localization by using fluent feature.

Allow the following attributes:

  • #[validate(..., fluent("message-id", key1 = value1, ...))]
  • #[validate(..., message_l10n = fluent("message-id", key1 = value1, ...))]
use unic_langid::LanguageIdentifier;
use serde_json::json;
use serde_valid::{fluent::Localize, Validate};


#[derive(Validate)]
struct Data (
    #[validate(min_length = 3, fluent("name-min-length", min_length = 3))]
    String,
);

assert_eq!(
    Data("田中".to_string()).validate()
        .unwrap_err()
        .localize(&get_bundle("name-min-length = 名前の長さは { $min_length } 文字以上でないといけません。"))
        .to_string(),
    json!({
        "errors": ["名前の長さは \u{2068}3\u{2069} 文字以上でないといけません。"]
    })
    .to_string()
);

§Custom Validation

§Single Error Validation

You can use your custom validation using by #[validate(custom = ...)].

use serde_valid::Validate;

fn user_validation(_val: &i32) -> Result<(), serde_valid::validation::Error> {
    Ok(())
}

#[derive(Validate)]
struct Data {
    #[validate(custom = user_validation)]
    val: i32,
}

let s = Data { val: 1 };

assert!(s.validate().is_ok());

And you can also use closure.

use serde_valid::Validate;

fn user_validation(_val: &i32, param1: bool) -> Result<(), serde_valid::validation::Error> {
    Ok(())
}

#[derive(Validate)]
struct Data {
    #[validate(custom = |v| user_validation(v, true))]
    val: i32,
}

let s = Data { val: 1 };

assert!(s.validate().is_ok());

Custom validation is suitable for handling convenience validations not defined in JSON Schema. serde_valid::utils::* provides convenience functions for specific types.

use serde_json::json;
use serde_valid::Validate;
use serde_valid::utils::{duration_maximum, duration_minimum};


#[derive(Validate)]
struct Data {
    #[validate(custom = duration_maximum(std::time::Duration::from_micros(5)))]
    #[validate(custom = duration_minimum(std::time::Duration::from_micros(0)))]
    val1: std::time::Duration,
}

let s = Data {
    val1: std::time::Duration::from_micros(1),
};

assert!(s.validate().is_ok());

§Multi Errors Validation

If you want to return multiple errors in the use custom validation method, you can use #[validate(custom = ...)] same as single error.

use serde_valid::Validate;

// 🚀 Just change the return type from `Result<(), Error>` to `Result<(), Vec<Error>>` !!
fn user_validation(_val: &i32) -> Result<(), Vec<serde_valid::validation::Error>> {
    Ok(())
}

#[derive(Validate)]
struct Data {
    #[validate(custom = user_validation)]
    val: i32,
}

let s = Data { val: 1 };

assert!(s.validate().is_ok());

§Multi Fields Validation

Now, you can use #[validate(custom = ...)] for multi fields validation.

use serde_json::json;
use serde_valid::Validate;

fn sample_validation(val1: i32, val2: &str) -> Result<(), serde_valid::validation::Error> {
    Ok(())
}

#[derive(Validate)]
#[validate(custom = |s| sample_validation(s.val1, &s.val2))]
struct Data {
    val1: i32,
    val2: String,
}

let s = Data {
    val1: 1,
    val2: "val2".to_owned(),
};

assert!(s.validate().is_ok());

§Validate Traits

By implementing the validation trait, Your original type can uses Serde Valid validations.

use serde_valid::Validate;

struct MyType(String);

impl serde_valid::ValidateMaxLength for MyType {
    fn validate_max_length(&self, max_length: usize) -> Result<(), serde_valid::MaxLengthError> {
        self.0.validate_max_length(max_length)
    }
}

#[derive(Validate)]
struct Data {
    #[validate(max_length = 5)]
    val: MyType,
}

let s = Data {
    val: MyType(String::from("😍👺🙋🏽👨‍🎤👨‍👩‍👧‍👦")),
};

assert!(s.validate().is_ok());

§Validation Errors Format

§Named Struct

Field errors are output to properties.

use serde_json::json;
use serde_valid::Validate;

#[derive(Validate)]
struct Data {
    #[validate(maximum = 4)]
    val: u32,
}

let s = Data { val: 5 };

assert_eq!(
    s.validate().unwrap_err().to_string(),
    json!({
        "errors": [],
        "properties": {
            "val": {
                "errors": ["The number must be `<= 4`."]
            }
        }
    })
    .to_string()
);

§Unnamed Struct

Field errors are output to items. The key for items is guaranteed to be a string of positive numbers.

use serde_json::json;
use serde_valid::Validate;

#[derive(Validate)]
struct Data (
    #[validate(maximum = 4)] u32,
    #[validate(maximum = 3)] u32,
);

let s = Data ( 5, 4 );

assert_eq!(
    s.validate().unwrap_err().to_string(),
    json!({
        "errors": [],
        "items": {
            "0": {
                "errors": ["The number must be `<= 4`."]
            },
            "1": {
                "errors": ["The number must be `<= 3`."]
            }
        }
    })
    .to_string()
);

§New Type

Field errors are output to errors.

use serde_json::json;
use serde_valid::Validate;

#[derive(Validate)]
struct Data (
    #[validate(maximum = 4)] u32
);

let s = Data (5);

assert_eq!(
    s.validate().unwrap_err().to_string(),
    json!({
        "errors": ["The number must be `<= 4`."]
    })
    .to_string()
);

§Named Enum

Variant errors are output to properties.

use serde_json::json;
use serde_valid::Validate;

#[derive(Validate)]
enum Data {
    Named {
        #[validate(maximum = 5)]
        a: i32,
        #[validate(maximum = 5)]
        b: i32,
    },
}

let s = Data::Named { a: 6, b: 6 };

assert_eq!(
    s.validate().unwrap_err().to_string(),
    json!({
        "errors": [],
        "properties": {
            "a": {
                "errors": ["The number must be `<= 5`."]
            },
            "b": {
                "errors": ["The number must be `<= 5`."]
            }
        }
    })
    .to_string()
);

§Unnamed Enum

Variant errors are output to items. The key for items is guaranteed to be a string of positive numbers.

use serde_json::json;
use serde_valid::Validate;

#[derive(Validate)]
enum Data {
    Unnamed (
        #[validate(maximum = 5)] i32,
        #[validate(maximum = 5)] i32,
    ),
}

let s = Data::Unnamed ( 6, 6 );

assert_eq!(
    s.validate().unwrap_err().to_string(),
    json!({
        "errors": [],
        "items": {
            "0": {
                "errors": ["The number must be `<= 5`."]
            },
            "1": {
                "errors": ["The number must be `<= 5`."]
            }
        }
    })
    .to_string()
);

§New Type Enum

Variant errors are output to errors.

use serde_json::json;
use serde_valid::Validate;

#[derive(Validate)]
enum Data {
    NewType (
        #[validate(maximum = 5)] i32,
    ),
}

let s = Data::NewType ( 6 );

assert_eq!(
    s.validate().unwrap_err().to_string(),
    json!({
        "errors": ["The number must be `<= 5`."]
    })
    .to_string()
);

Re-exports§

pub use error::EnumerateError;
pub use error::Error;
pub use error::ExclusiveMaximumError;
pub use error::ExclusiveMinimumError;
pub use error::MaxItemsError;
pub use error::MaxLengthError;
pub use error::MaxPropertiesError;
pub use error::MaximumError;
pub use error::MinItemsError;
pub use error::MinLengthError;
pub use error::MinPropertiesError;
pub use error::MinimumError;
pub use error::MultipleOfError;
pub use error::PatternError;
pub use error::UniqueItemsError;
pub use validation::ValidateEnumerate;
pub use validation::ValidateExclusiveMaximum;
pub use validation::ValidateExclusiveMinimum;
pub use validation::ValidateMaxItems;
pub use validation::ValidateMaxLength;
pub use validation::ValidateMaxProperties;
pub use validation::ValidateMaximum;
pub use validation::ValidateMinItems;
pub use validation::ValidateMinLength;
pub use validation::ValidateMinProperties;
pub use validation::ValidateMinimum;
pub use validation::ValidateMultipleOf;
pub use validation::ValidatePattern;
pub use validation::ValidateUniqueItems;

Modules§

error
export
json
utils
validation

Traits§

Validate

Derive Macros§

Validate