Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use BitMaps to validate multiple types #78

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 6 additions & 32 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::primitive_type::{PrimitiveType, PrimitiveTypesBitMap};
use serde_json::Value;
use std::{
borrow::Cow,
Expand Down Expand Up @@ -127,37 +128,10 @@ pub enum ValidationErrorKind {
UnknownReferenceScheme { scheme: String },
}

/// For faster error handling in "type" keyword validator we have this enum, to match
/// with it instead of a string.
#[derive(Debug, Clone)]
pub enum PrimitiveType {
Integer,
Null,
Boolean,
String,
Array,
Object,
Number,
}

impl fmt::Display for PrimitiveType {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
match self {
PrimitiveType::Integer => write!(f, "integer"),
PrimitiveType::Null => write!(f, "null"),
PrimitiveType::Boolean => write!(f, "boolean"),
PrimitiveType::String => write!(f, "string"),
PrimitiveType::Array => write!(f, "array"),
PrimitiveType::Object => write!(f, "object"),
PrimitiveType::Number => write!(f, "number"),
}
}
}

#[derive(Debug)]
pub enum TypeKind {
Single(PrimitiveType),
Multiple(Vec<PrimitiveType>),
Multiple(PrimitiveTypesBitMap),
}

/// Shortcuts for creation of specific error kinds.
Expand Down Expand Up @@ -366,7 +340,7 @@ impl<'a> ValidationError<'a> {
}
pub(crate) fn multiple_type_error(
instance: &'a Value,
types: Vec<PrimitiveType>,
types: PrimitiveTypesBitMap,
) -> ValidationError<'a> {
ValidationError {
instance: Cow::Borrowed(instance),
Expand Down Expand Up @@ -597,7 +571,7 @@ impl<'a> fmt::Display for ValidationError<'a> {
"'{}' is not of types {}",
self.instance,
types
.iter()
.into_iter()
.map(|t| format!("'{}'", t))
.collect::<Vec<String>>()
.join(", ")
Expand All @@ -624,9 +598,9 @@ mod tests {
let instance = json!(42);
let err = ValidationError::multiple_type_error(
&instance,
vec![PrimitiveType::String, PrimitiveType::Number],
vec![PrimitiveType::String, PrimitiveType::Number].into(),
);
let repr = format!("{}", err);
assert_eq!(repr, "'42' is not of types 'string', 'number'")
assert_eq!(repr, "'42' is not of types 'number', 'string'")
}
}
70 changes: 33 additions & 37 deletions src/keywords/legacy/type_draft_4.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
use super::super::{type_, CompilationResult, Validate};
use crate::{
compilation::{CompilationContext, JSONSchema},
error::{error, no_error, CompilationError, ErrorIterator, PrimitiveType, ValidationError},
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
primitive_type::{PrimitiveType, PrimitiveTypesBitMap},
};
use serde_json::{Map, Number, Value};
use std::convert::TryFrom;

pub struct MultipleTypesValidator {
types: Vec<PrimitiveType>,
types: PrimitiveTypesBitMap,
}

impl MultipleTypesValidator {
#[inline]
pub(crate) fn compile(items: &[Value]) -> CompilationResult {
let mut types = Vec::with_capacity(items.len());
let mut types = PrimitiveTypesBitMap::new();
for item in items {
match item {
Value::String(string) => match string.as_str() {
"integer" => types.push(PrimitiveType::Integer),
"null" => types.push(PrimitiveType::Null),
"boolean" => types.push(PrimitiveType::Boolean),
"string" => types.push(PrimitiveType::String),
"array" => types.push(PrimitiveType::Array),
"object" => types.push(PrimitiveType::Object),
"number" => types.push(PrimitiveType::Number),
_ => return Err(CompilationError::SchemaError),
},
Value::String(string) => {
if let Ok(primitive_type) = PrimitiveType::try_from(string.as_str()) {
types |= primitive_type;
} else {
return Err(CompilationError::SchemaError);
}
}
_ => return Err(CompilationError::SchemaError),
}
}
Expand All @@ -39,32 +38,29 @@ impl Validate for MultipleTypesValidator {
} else {
error(ValidationError::multiple_type_error(
instance,
self.types.clone(),
self.types,
))
}
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
for type_ in &self.types {
match (type_, instance) {
(PrimitiveType::Integer, Value::Number(num)) if is_integer(num) => return true,
(PrimitiveType::Null, Value::Null)
| (PrimitiveType::Boolean, Value::Bool(_))
| (PrimitiveType::String, Value::String(_))
| (PrimitiveType::Array, Value::Array(_))
| (PrimitiveType::Object, Value::Object(_))
| (PrimitiveType::Number, Value::Number(_)) => return true,
(_, _) => continue,
};
match instance {
Value::Array(_) => self.types.contains_type(&PrimitiveType::Array),
Value::Bool(_) => self.types.contains_type(&PrimitiveType::Boolean),
Value::Null => self.types.contains_type(&PrimitiveType::Null),
Value::Number(num) => {
self.types.contains_type(&PrimitiveType::Number)
|| (self.types.contains_type(&PrimitiveType::Integer) && is_integer(num))
}
Value::Object(_) => self.types.contains_type(&PrimitiveType::Object),
Value::String(_) => self.types.contains_type(&PrimitiveType::String),
}
false
}

fn name(&self) -> String {
format!(
"type: [{}]",
self.types
.iter()
.map(|type_| format!("{}", type_))
.into_iter().map(|type_| format!("{}", type_))
.collect::<Vec<String>>()
.join(", ")
)
Expand Down Expand Up @@ -132,14 +128,14 @@ pub fn compile(
}

fn compile_single_type(item: &str) -> Option<CompilationResult> {
match item {
"integer" => Some(IntegerTypeValidator::compile()),
"null" => Some(type_::NullTypeValidator::compile()),
"boolean" => Some(type_::BooleanTypeValidator::compile()),
"string" => Some(type_::StringTypeValidator::compile()),
"array" => Some(type_::ArrayTypeValidator::compile()),
"object" => Some(type_::ObjectTypeValidator::compile()),
"number" => Some(type_::NumberTypeValidator::compile()),
_ => Some(Err(CompilationError::SchemaError)),
match PrimitiveType::try_from(item) {
Ok(PrimitiveType::Array) => Some(type_::ArrayTypeValidator::compile()),
Ok(PrimitiveType::Boolean) => Some(type_::BooleanTypeValidator::compile()),
Ok(PrimitiveType::Integer) => Some(IntegerTypeValidator::compile()),
Ok(PrimitiveType::Null) => Some(type_::NullTypeValidator::compile()),
Ok(PrimitiveType::Number) => Some(type_::NumberTypeValidator::compile()),
Ok(PrimitiveType::Object) => Some(type_::ObjectTypeValidator::compile()),
Ok(PrimitiveType::String) => Some(type_::StringTypeValidator::compile()),
Err(()) => Some(Err(CompilationError::SchemaError)),
}
}
69 changes: 33 additions & 36 deletions src/keywords/type_.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
use super::{CompilationResult, Validate};
use crate::{
compilation::{CompilationContext, JSONSchema},
error::{error, no_error, CompilationError, ErrorIterator, PrimitiveType, ValidationError},
error::{error, no_error, CompilationError, ErrorIterator, ValidationError},
primitive_type::{PrimitiveType, PrimitiveTypesBitMap},
};
use serde_json::{Map, Number, Value};
use std::convert::TryFrom;

pub struct MultipleTypesValidator {
types: Vec<PrimitiveType>,
types: PrimitiveTypesBitMap,
}

impl MultipleTypesValidator {
#[inline]
pub(crate) fn compile(items: &[Value]) -> CompilationResult {
let mut types = Vec::with_capacity(items.len());
let mut types = PrimitiveTypesBitMap::new();
for item in items {
match item {
Value::String(string) => match string.as_str() {
"integer" => types.push(PrimitiveType::Integer),
"null" => types.push(PrimitiveType::Null),
"boolean" => types.push(PrimitiveType::Boolean),
"string" => types.push(PrimitiveType::String),
"array" => types.push(PrimitiveType::Array),
"object" => types.push(PrimitiveType::Object),
"number" => types.push(PrimitiveType::Number),
_ => return Err(CompilationError::SchemaError),
},
Value::String(string) => {
if let Ok(primitive_type) = PrimitiveType::try_from(string.as_str()) {
types |= primitive_type;
} else {
return Err(CompilationError::SchemaError);
}
}
_ => return Err(CompilationError::SchemaError),
}
}
Expand All @@ -39,31 +38,29 @@ impl Validate for MultipleTypesValidator {
} else {
error(ValidationError::multiple_type_error(
instance,
self.types.clone(),
self.types,
))
}
}
fn is_valid(&self, _: &JSONSchema, instance: &Value) -> bool {
for type_ in &self.types {
match (type_, instance) {
(PrimitiveType::Integer, Value::Number(num)) if is_integer(num) => return true,
(PrimitiveType::Null, Value::Null)
| (PrimitiveType::Boolean, Value::Bool(_))
| (PrimitiveType::String, Value::String(_))
| (PrimitiveType::Array, Value::Array(_))
| (PrimitiveType::Object, Value::Object(_))
| (PrimitiveType::Number, Value::Number(_)) => return true,
(_, _) => continue,
};
match instance {
Value::Array(_) => self.types.contains_type(&PrimitiveType::Array),
Value::Bool(_) => self.types.contains_type(&PrimitiveType::Boolean),
Value::Null => self.types.contains_type(&PrimitiveType::Null),
Value::Number(num) => {
self.types.contains_type(&PrimitiveType::Number)
|| (self.types.contains_type(&PrimitiveType::Integer) && is_integer(num))
}
Value::Object(_) => self.types.contains_type(&PrimitiveType::Object),
Value::String(_) => self.types.contains_type(&PrimitiveType::String),
}
false
}

fn name(&self) -> String {
format!(
"type: [{}]",
self.types
.iter()
.into_iter()
.map(|type_| format!("{}", type_))
.collect::<Vec<String>>()
.join(", ")
Expand Down Expand Up @@ -308,14 +305,14 @@ pub fn compile(
}

fn compile_single_type(item: &str) -> Option<CompilationResult> {
match item {
"integer" => Some(IntegerTypeValidator::compile()),
"null" => Some(NullTypeValidator::compile()),
"boolean" => Some(BooleanTypeValidator::compile()),
"string" => Some(StringTypeValidator::compile()),
"array" => Some(ArrayTypeValidator::compile()),
"object" => Some(ObjectTypeValidator::compile()),
"number" => Some(NumberTypeValidator::compile()),
_ => Some(Err(CompilationError::SchemaError)),
match PrimitiveType::try_from(item) {
Ok(PrimitiveType::Array) => Some(ArrayTypeValidator::compile()),
Ok(PrimitiveType::Boolean) => Some(BooleanTypeValidator::compile()),
Ok(PrimitiveType::Integer) => Some(IntegerTypeValidator::compile()),
Ok(PrimitiveType::Null) => Some(NullTypeValidator::compile()),
Ok(PrimitiveType::Number) => Some(NumberTypeValidator::compile()),
Ok(PrimitiveType::Object) => Some(ObjectTypeValidator::compile()),
Ok(PrimitiveType::String) => Some(StringTypeValidator::compile()),
Err(()) => Some(Err(CompilationError::SchemaError)),
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
mod compilation;
mod error;
mod keywords;
mod primitive_type;
mod resolver;
mod schemas;
pub use compilation::JSONSchema;
Expand Down
Loading