Skip to content

Commit

Permalink
🚧 Continue working on schema validation
Browse files Browse the repository at this point in the history
  • Loading branch information
rster2002 committed Oct 26, 2023
1 parent b15af38 commit 64dd723
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 10 deletions.
7 changes: 7 additions & 0 deletions src/field.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
use serde::{Deserialize, Serialize};
use crate::schema_type::SchemaType;

#[derive(Debug, Serialize, Deserialize)]
pub struct Field {
#[serde(rename = "type")]
kind: SchemaType,
label: String,
description: Option<String>,
}
1 change: 1 addition & 0 deletions src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub enum SchemaValidationError {
InvalidSchemaValue,
}

/// A schema encapsulates multiple version of the schema which are updated through migrations.
#[derive(Debug)]
pub struct Schema {
inner: Value,
Expand Down
33 changes: 31 additions & 2 deletions src/schema_type.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::schema_type::advanced_type::AdvancedType;
use crate::schema_type::basic_type::BasicType;
use serde_json::Value;
use thiserror::Error;
use crate::schema_type::advanced_type::{AdvancedType, AdvancedTypeValidationError};
use crate::schema_type::basic_type::{BasicType, BasicTypeValidationError};
use crate::traits::validator::Validator;

pub mod basic_type;
pub mod advanced_type;
Expand All @@ -16,6 +19,32 @@ pub enum SchemaType {
Object(HashMap<String, SchemaType>),
}

#[derive(Debug, Error, PartialEq)]
pub enum SchemaTypeValidationError {
#[error("{0}")]
BasicTypeValidationError(#[from] BasicTypeValidationError),

#[error("{0}")]
AdvancedTypeValidationError(#[from] AdvancedTypeValidationError),
}

impl Validator for SchemaType {
type E = SchemaTypeValidationError;

fn validate(&self, value: &Value) -> Result<(), Self::E> {
match self {
SchemaType::Basic(basic_type) => Ok(basic_type.validate(value)?),
SchemaType::Advanced(advanced_type) => Ok(advanced_type.validate(value)?),
SchemaType::Array(_) => {
todo!()
}
SchemaType::Object(_) => {
todo!()
}
}
}
}

#[cfg(test)]
mod tests {
use std::collections::HashMap;
Expand Down
42 changes: 41 additions & 1 deletion src/schema_type/advanced_type.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,51 @@
pub mod advanced_string_type;

use serde::{Deserialize, Serialize};
use crate::schema_type::advanced_type::advanced_string_type::AdvancedStringType;
use serde_json::Value;
use thiserror::Error;
use crate::schema_type::advanced_type::advanced_string_type::{AdvancedStringType, StringValidationError};
use crate::schema_type::SchemaType;
use crate::traits::validator::Validator;

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "$", rename_all = "camelCase")]
#[cfg_attr(test, derive(PartialEq))]
pub enum AdvancedType {
String(AdvancedStringType),
Optional(Box<SchemaType>),
}

#[derive(Debug, PartialEq, Error)]
pub enum AdvancedTypeValidationError {
#[error("{0}")]
StringValidationError(#[from] StringValidationError),
}

impl Validator for AdvancedType {
type E = AdvancedTypeValidationError;

fn validate(&self, value: &Value) -> Result<(), Self::E> {
match self {
AdvancedType::String(advanced_string) => Ok(advanced_string.validate(value)?),
AdvancedType::Optional(nested_type) => {
if let Value::Null = value {
return Ok(())
}

todo!()
}
}
}
}

#[cfg(test)]
mod tests {
use crate::schema_type::advanced_type::AdvancedType;
use crate::schema_type::basic_type::BasicType;
use crate::schema_type::SchemaType;

#[test]
fn optional_advanced_type_is_resolved_correctly() {
let advanced_type = AdvancedType::Optional(Box::new(SchemaType::Basic(BasicType::String)));
}
}
98 changes: 94 additions & 4 deletions src/schema_type/advanced_type/advanced_string_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,33 @@ use serde_json::Value;
use thiserror::Error;
use crate::traits::validator::Validator;

#[derive(Debug, Default, Serialize, Deserialize)]
/// Helper function to let Serde set a default value of `true`. Check this
/// [GitHub issue](https://github.com/serde-rs/serde/issues/368) for more information.
fn default_true() -> bool {
true
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, derive(PartialEq))]
pub struct AdvancedStringType {
pub require_filled: Option<bool>,
#[serde(default = "default_true")]
pub require_filled: bool,
pub min_length: Option<usize>,
pub max_length: Option<usize>,
}

#[derive(Debug, Error)]
impl Default for AdvancedStringType {
fn default() -> Self {
Self {
require_filled: true,
min_length: None,
max_length: None,
}
}
}

#[derive(Debug, PartialEq, Error)]
pub enum StringValidationError {
#[error("The provided value is not a string")]
NotAString,
Expand All @@ -35,7 +52,7 @@ impl Validator for AdvancedStringType {
return Err(StringValidationError::NotAString);
};

if string.is_empty() && self.require_filled.unwrap_or(true) {
if string.is_empty() && self.require_filled {
return Err(StringValidationError::RequireFilled);
}

Expand All @@ -54,3 +71,76 @@ impl Validator for AdvancedStringType {
Ok(())
}
}

#[cfg(test)]
mod tests {
use serde_json::json;
use crate::schema_type::advanced_type::advanced_string_type::{AdvancedStringType, StringValidationError};
use crate::traits::validator::Validator;

#[test]
fn advanced_string_type_requires_filled_string_by_default() {
assert_eq!(AdvancedStringType::default().validate(&json!("")), Err(StringValidationError::RequireFilled));
}

#[test]
fn advanced_string_type_requires_filled_is_checked_correctly() {
assert_eq!(AdvancedStringType {
require_filled: true,
..AdvancedStringType::default()
}.validate(&json!("")), Err(StringValidationError::RequireFilled));

assert_eq!(AdvancedStringType {
require_filled: false,
..AdvancedStringType::default()
}.validate(&json!("")), Ok(()));
}

#[test]
fn advanced_string_type_min_length_is_checked_correctly() {
assert_eq!(AdvancedStringType {
require_filled: false,
min_length: Some(0),
..AdvancedStringType::default()
}.validate(&json!("")), Ok(()));

assert_eq!(AdvancedStringType {
min_length: Some(5),
..AdvancedStringType::default()
}.validate(&json!("abcde")), Ok(()));

assert_eq!(AdvancedStringType {
min_length: Some(5),
..AdvancedStringType::default()
}.validate(&json!("abcdef")), Ok(()));

assert_eq!(AdvancedStringType {
min_length: Some(5),
..AdvancedStringType::default()
}.validate(&json!("abcd")), Err(StringValidationError::StringTooShort));
}

#[test]
fn advanced_string_type_max_length_is_checked_correctly() {
assert_eq!(AdvancedStringType {
require_filled: false,
max_length: Some(0),
..AdvancedStringType::default()
}.validate(&json!("")), Ok(()));

assert_eq!(AdvancedStringType {
max_length: Some(5),
..AdvancedStringType::default()
}.validate(&json!("abcde")), Ok(()));

assert_eq!(AdvancedStringType {
max_length: Some(5),
..AdvancedStringType::default()
}.validate(&json!("abcd")), Ok(()));

assert_eq!(AdvancedStringType {
max_length: Some(5),
..AdvancedStringType::default()
}.validate(&json!("abcdef")), Err(StringValidationError::StringTooLong));
}
}
6 changes: 3 additions & 3 deletions src/schema_type/basic_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use thiserror::Error;
use uuid::{Error, Uuid};
use crate::traits::validator::Validator;

#[derive(Debug, Error)]
#[derive(Debug, PartialEq, Error)]
pub enum BasicTypeValidationError {
#[error("Incorrect type provided. Expected '{0}' but got '{1}'")]
IncorrectType(BasicType, Value),
Expand All @@ -19,9 +19,8 @@ pub enum BasicTypeValidationError {
IncorrectEmail(String),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(test, derive(PartialEq))]
pub enum BasicType {
String,
Number,
Expand Down Expand Up @@ -164,6 +163,7 @@ mod tests {
#[test]
fn basic_email_type_is_validated_correctly() {
assert!(BasicType::Email.validate(&json!("[email protected]")).is_ok());
assert!(BasicType::Email.validate(&json!("[email protected]")).is_ok());

assert!(BasicType::Email.validate(&json!("f1df9904-6f6b-4157-8a82-b1a566a50ec2")).is_err());
assert!(BasicType::Email.validate(&json!("")).is_err());
Expand Down

0 comments on commit 64dd723

Please sign in to comment.