Skip to content

yassun7010/serde_valid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

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.

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

In addition, [serde_valid::utils][module@crate::utils] provides a type of validation not described in the JSON schema specification.

Type Serde Valid (validate derive) Serde Valid (validation function)
Duration #[validate(custom = duration_maximum(SECOND))] [duration_maximum][crate::utils::duration_maximum]
Duration #[validate(custom = duration_minimum(ZERO))] [duration_minimum][crate::utils::duration_minimum]
Duration #[validate(custom = duration_exclusive_maximum(SECOND))] [duration_exclusive_maximum][crate::utils::duration_exclusive_maximum]
Duration #[validate(custom = duration_exclusive_minimum(ZERO))] [duration_exclusive_minimum][crate::utils::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()
);