Skip to content

yassun7010/serde_valid

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