45 releases

0.19.0 Mar 2, 2024
0.17.0 Jan 13, 2024
0.16.3 Jul 30, 2023
0.15.0 Mar 19, 2023
0.6.2 Jul 31, 2022

#81 in Encoding

Download history 1790/week @ 2024-01-03 1609/week @ 2024-01-10 2398/week @ 2024-01-17 2744/week @ 2024-01-24 2358/week @ 2024-01-31 2459/week @ 2024-02-07 2499/week @ 2024-02-14 2751/week @ 2024-02-21 4178/week @ 2024-02-28 5644/week @ 2024-03-06 4609/week @ 2024-03-13 3787/week @ 2024-03-20 2993/week @ 2024-03-27 3399/week @ 2024-04-03 3254/week @ 2024-04-10 2362/week @ 2024-04-17

12,575 downloads per month
Used in 6 crates (5 directly)

MIT license

220KB
4.5K SLoC

Serde Valid

Latest Version GitHub license

This is JSON Schema based validation tool using with 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).
  • flatten - change formatting to flattened error messages ( jsonschema crate style).

Validations

Serde Valid support standard validation based JSON Schema.

Type Serde Valid(validate derive) Serde Valid(validate trait) Json Schema
String #[validate(max_length = 5)] ValidateMaxLength maxLength
String #[validate(min_length = 5)] ValidateMinLength minLength
String #[validate(pattern = r"^\d{5}$")] ValidatePattern pattern
Numeric #[validate(maximum = 5)] ValidateMaximum maximum
Numeric #[validate(minimum = 5)] ValidateMinimum minimum
Numeric #[validate(exclusive_maximum = 5)] ValidateExclusiveMaximum exclusiveMaximum
Numeric #[validate(exclusive_minimum = 5)] ValidateExclusiveMinimum exclusiveMinimum
Numeric #[validate(multiple_of = 5)] ValidateMultipleOf multipleOf
Object #[validate(max_properties = 5)] ValidateMaxProperties maxProperties
Object #[validate(min_properties = 5)] ValidateMinProperties minProperties
Array #[validate(max_items = 5)] ValidateMaxItems maxItems
Array #[validate(min_items = 5)] ValidateMinItems minItems
Array #[validate(unique_items)] ValidateUniqueItems uniqueItems
Generic #[validate(enumerate(5, 10, 15))] ValidateEnumerate enum

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.as_validation_errors().unwrap().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 min 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 max 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 min custom message_fn.",
                    "this is max custom message."
                ]
            }
        }
    })
    .to_string()
);

Fluent localization

You can also use fluent localization by using fluent feature.

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 method

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());

Multi Fields Validation

Custom 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());

Rules

๐Ÿšธ Warning ๐Ÿšธ : this feature is deprecated. Please use #[validate(custom)] instead.

If you want to check multi fields validation, can use #[rule].

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

fn sample_rule(_val1: &i32, _val2: &str) -> Result<(), serde_valid::validation::Error> {
    Err(serde_valid::validation::Error::Custom(
        "Rule error.".to_owned(),
    ))
}

#[derive(Validate)]
#[rule(sample_rule(val2, val1))]
struct Data {
    val1: String,
    val2: i32,
}

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

let errors = s.validate().unwrap_err();

assert_eq!(
    errors.to_string(),
    json!({
        "errors": ["Rule error."],
        "properties": {}
    })
    .to_string()
);

If you want to use rule to unnamed fields struct, just like this,

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

fn sample_rule(_val1: &i32, _val2: &str) -> Result<(), serde_valid::validation::Error> {
    Ok(())
}

#[derive(Validate)]
#[rule(sample_rule(0, 1))]
struct Data(i32, String);

let s = Data(0, "1".to_owned());

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

And you can also use closure.

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

fn sample_rule(_val1: &i32, _val2: &str) -> Result<(), serde_valid::validation::Error> {
    Ok(())
}

#[derive(Validate)]
#[rule(|val1, val2| sample_rule(val2, val1))]
struct Data {
    val1: String,
    val2: i32,
}

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

assert!(s.validate().is_ok());
use serde_json::json;
use serde_valid::Validate;

fn sample_rule(_val1: &i32, _val2: &str) -> Result<(), serde_valid::validation::Error> {
    Ok(())
}

#[derive(Validate)]
#[rule(|_0, _1| sample_rule(_0, _1))]
struct Data(i32, String);

let s = Data(0, "1".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()
);

Newtype 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()
);

Dependencies

~5โ€“15MB
~162K SLoC