serde_valid 2.0.1

JSON Schema based validation tool using serde.
Documentation
use serde_json::json;
use serde_valid::Validate;

mod parenthesized {
    pub fn meta_path_validation(_val: &[i32]) -> Result<(), serde_valid::validation::Error> {
        Ok(())
    }
}

#[test]
fn custom_validation_is_ok() {
    fn user_validation(_val: &i32) -> Result<(), serde_valid::validation::Error> {
        Ok(())
    }

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

    let s = TestStruct { val: 1 };
    assert!(s.validate().is_ok());
}

#[test]
fn custom_parenthesized_path_validation_is_ok() {
    #[derive(Validate)]
    struct TestStruct {
        #[validate(custom = parenthesized::meta_path_validation)]
        val: Vec<i32>,
    }

    let s = TestStruct {
        val: vec![1, 2, 3, 4],
    };
    assert!(s.validate().is_ok());
}

#[test]
fn custom_clouser_validation_is_ok() {
    fn user_validation(_val: &[i32], _maximum: i32) -> Result<(), serde_valid::validation::Error> {
        Ok(())
    }

    #[derive(Validate)]
    struct TestStruct {
        #[validate(custom = |x| user_validation(x, 10))]
        val: Vec<i32>,
    }

    let s = TestStruct {
        val: vec![1, 2, 3, 4],
    };
    assert!(s.validate().is_ok());
}

#[test]
fn custom_clouser_validation_is_err() {
    fn user_validation(val: &[i32], maximum: i32) -> Result<(), serde_valid::validation::Error> {
        if val.iter().all(|v| v <= &maximum) {
            Ok(())
        } else {
            Err(serde_valid::validation::Error::Custom(
                "this is custom message.".to_string(),
            ))
        }
    }

    #[derive(Validate)]
    struct TestStruct {
        #[validate(custom = |x| user_validation(x, 10))]
        val: Vec<i32>,
    }

    let s = TestStruct {
        val: vec![1, 2, 3, 11],
    };
    assert!(s.validate().is_err());
}

#[test]
fn custom_validation_error() {
    fn user_validation(_val: &[i32]) -> Result<(), serde_valid::validation::Error> {
        Err(serde_valid::validation::Error::Custom(
            "this is custom message.".to_string(),
        ))
    }

    #[derive(Validate)]
    struct TestStruct {
        #[validate(custom = user_validation)]
        val: Vec<i32>,
    }

    let s = TestStruct {
        val: vec![1, 2, 3, 4],
    };
    assert_eq!(
        s.validate().unwrap_err().to_string(),
        json!({
            "errors": [],
            "properties": {
                "val": {
                    "errors": [
                        "this is custom message."
                    ]
                }
            }
        })
        .to_string()
    );
}

#[test]
fn named_struct_custom_is_ok() {
    fn sample_struct_validation(_val: &TestStruct) -> Result<(), serde_valid::validation::Error> {
        Ok(())
    }

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

    let s = TestStruct { val: 5 };
    assert_eq!(s.val, 5);
    assert!(s.validate().is_ok());
}

#[test]
fn named_struct_custom_closure_is_ok() {
    fn sample_struct_validation(_val: i32) -> Result<(), serde_valid::validation::Error> {
        Ok(())
    }

    #[derive(Validate)]
    #[validate(custom = |s| sample_struct_validation(s.val))]
    struct TestStruct {
        val: i32,
    }

    let s = TestStruct { val: 5 };
    assert_eq!(s.val, 5);
    assert!(s.validate().is_ok());
}

#[test]
fn unnamed_struct_custom_is_ok() {
    fn sample_struct_validation(_val: &TestStruct) -> Result<(), serde_valid::validation::Error> {
        Ok(())
    }

    #[derive(Validate)]
    #[validate(custom = sample_struct_validation)]
    struct TestStruct(i32);

    let s = TestStruct(5);
    assert_eq!(s.0, 5);
    assert!(s.validate().is_ok());
}

#[test]
fn unnamed_struct_custom_closure_is_ok() {
    fn sample_struct_validation(_val: i32) -> Result<(), serde_valid::validation::Error> {
        Ok(())
    }

    #[derive(Validate)]
    #[validate(custom = |s| sample_struct_validation(s.0))]
    struct TestStruct(i32);

    let s = TestStruct(5);
    assert_eq!(s.0, 5);
    assert!(s.validate().is_ok());
}

#[test]
fn unnamed_struct_custom_closure_is_err() {
    fn sample_struct_validation(_val: i32) -> Result<(), serde_valid::validation::Error> {
        Err(serde_valid::validation::Error::Custom(
            "Struct Validation Error.".to_owned(),
        ))
    }

    #[derive(Validate)]
    #[validate(custom = |s| sample_struct_validation(s.0))]
    struct TestStruct(i32);

    let s = TestStruct(5);
    assert_eq!(s.0, 5);
    assert!(s.validate().is_err());
}

#[test]
fn named_struct_custom_vec_errors_is_ok() {
    fn validation(_val: &TestStruct) -> Result<(), Vec<serde_valid::validation::Error>> {
        Ok(())
    }

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

    let s = TestStruct { val: 5 };
    assert_eq!(s.val, 5);
    assert!(s.validate().is_ok());
}

#[test]
fn named_struct_custom_vec_errors_is_err() {
    fn validation(_val: &TestStruct) -> Result<(), Vec<serde_valid::validation::Error>> {
        Err(vec![
            serde_valid::validation::Error::Custom("Error 1".to_owned()),
            serde_valid::validation::Error::Custom("Error 2".to_owned()),
        ])
    }

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

    let s = TestStruct { val: 5 };

    assert_eq!(s.val, 5);
    assert_eq!(
        s.validate().unwrap_err().to_string(),
        json!({
            "errors": ["Error 1", "Error 2"],
            "properties": {}
        })
        .to_string()
    );
}

#[test]
fn named_struct_custom_closure_vec_errors_is_ok() {
    fn sample_struct_validation(_val: i32) -> Result<(), Vec<serde_valid::validation::Error>> {
        Ok(())
    }

    #[derive(Validate)]
    #[validate(custom = |s| sample_struct_validation(s.val))]
    struct TestStruct {
        val: i32,
    }

    let s = TestStruct { val: 5 };
    assert_eq!(s.val, 5);
    assert!(s.validate().is_ok());
}

#[test]
fn named_struct_custom_closure_vec_errors_is_err() {
    fn sample_struct_validation(_val: i32) -> Result<(), Vec<serde_valid::validation::Error>> {
        Err(vec![
            serde_valid::validation::Error::Custom("Error 1".to_owned()),
            serde_valid::validation::Error::Custom("Error 2".to_owned()),
        ])
    }

    #[derive(Validate)]
    #[validate(custom = |s| sample_struct_validation(s.val))]
    struct TestStruct {
        val: i32,
    }

    let s = TestStruct { val: 5 };
    assert_eq!(s.val, 5);
    assert_eq!(
        s.validate().unwrap_err().to_string(),
        json!({
            "errors": ["Error 1", "Error 2"],
            "properties": {}
        })
        .to_string()
    );
}

#[test]
fn filed_custom_validation_using_self() {
    fn food_validation(kind: &str, food: &str) -> Result<(), serde_valid::validation::Error> {
        match kind {
            "Cat" => {
                if food == "CatFood" {
                    Ok(())
                } else {
                    Err(serde_valid::validation::Error::Custom(
                        "Cat should eat CatFood.".to_string(),
                    ))
                }
            }
            "Dog" => {
                if food == "DogFood" {
                    Ok(())
                } else {
                    Err(serde_valid::validation::Error::Custom(
                        "Dog should eat DogFood.".to_string(),
                    ))
                }
            }
            _ => Ok(()),
        }
    }

    #[derive(Validate)]
    struct Pet {
        #[validate(enumerate = ["Cat", "Dog"])]
        kind: String,

        #[validate(custom = |food| food_validation(&self.kind, food))]
        food: String,
    }

    let invalid = Pet {
        kind: "Cat".to_string(),
        food: "DogFood".to_string(),
    };

    assert_eq!(
        invalid.validate().unwrap_err().to_string(),
        json!({
            "errors": [],
            "properties": {
                "food": {
                    "errors": [
                        "Cat should eat CatFood."
                    ]
                }
            }
        })
        .to_string()
    );
}