jsonschema-rs/jsonschema/src/primitive_type.rs

181 lines
5.3 KiB
Rust

//! Primitive types for property type validators
use serde_json::Value;
use std::{convert::TryFrom, fmt, ops::BitOrAssign};
/// For faster error handling in "type" keyword validator we have this enum, to match
/// with it instead of a string.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[allow(missing_docs)]
pub enum PrimitiveType {
Array,
Boolean,
Integer,
Null,
Number,
Object,
String,
}
impl fmt::Display for PrimitiveType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PrimitiveType::Array => f.write_str("array"),
PrimitiveType::Boolean => f.write_str("boolean"),
PrimitiveType::Integer => f.write_str("integer"),
PrimitiveType::Null => f.write_str("null"),
PrimitiveType::Number => f.write_str("number"),
PrimitiveType::Object => f.write_str("object"),
PrimitiveType::String => f.write_str("string"),
}
}
}
impl TryFrom<&str> for PrimitiveType {
type Error = ();
#[inline]
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"array" => Ok(PrimitiveType::Array),
"boolean" => Ok(PrimitiveType::Boolean),
"integer" => Ok(PrimitiveType::Integer),
"null" => Ok(PrimitiveType::Null),
"number" => Ok(PrimitiveType::Number),
"object" => Ok(PrimitiveType::Object),
"string" => Ok(PrimitiveType::String),
_ => Err(()),
}
}
}
impl From<&Value> for PrimitiveType {
fn from(instance: &Value) -> Self {
match instance {
Value::Null => PrimitiveType::Null,
Value::Bool(_) => PrimitiveType::Boolean,
Value::Number(_) => PrimitiveType::Number,
Value::String(_) => PrimitiveType::String,
Value::Array(_) => PrimitiveType::Array,
Value::Object(_) => PrimitiveType::Object,
}
}
}
const fn primitive_type_to_bit_map_representation(primitive_type: PrimitiveType) -> u8 {
match primitive_type {
PrimitiveType::Array => 1,
PrimitiveType::Boolean => 2,
PrimitiveType::Integer => 4,
PrimitiveType::Null => 8,
PrimitiveType::Number => 16,
PrimitiveType::Object => 32,
PrimitiveType::String => 64,
}
}
fn bit_map_representation_primitive_type(bit_representation: u8) -> PrimitiveType {
match bit_representation {
1 => PrimitiveType::Array,
2 => PrimitiveType::Boolean,
4 => PrimitiveType::Integer,
8 => PrimitiveType::Null,
16 => PrimitiveType::Number,
32 => PrimitiveType::Object,
64 => PrimitiveType::String,
_ => unreachable!("This should never be possible"),
}
}
/// Compact representation of multiple `PrimitiveType`
#[derive(Clone, Copy, Debug)]
pub struct PrimitiveTypesBitMap {
inner: u8,
}
impl PrimitiveTypesBitMap {
pub(crate) const fn new() -> Self {
Self { inner: 0 }
}
#[inline]
pub(crate) const fn add_type(mut self, primitive_type: PrimitiveType) -> Self {
self.inner |= primitive_type_to_bit_map_representation(primitive_type);
self
}
pub(crate) const fn contains_type(self, primitive_type: PrimitiveType) -> bool {
primitive_type_to_bit_map_representation(primitive_type) & self.inner != 0
}
}
impl BitOrAssign<PrimitiveType> for PrimitiveTypesBitMap {
#[inline]
fn bitor_assign(&mut self, rhs: PrimitiveType) {
*self = self.add_type(rhs);
}
}
impl IntoIterator for PrimitiveTypesBitMap {
type Item = PrimitiveType;
type IntoIter = PrimitiveTypesBitMapIterator;
fn into_iter(self) -> Self::IntoIter {
PrimitiveTypesBitMapIterator {
idx: 0,
bit_map: self,
}
}
}
#[cfg(test)]
impl From<Vec<PrimitiveType>> for PrimitiveTypesBitMap {
fn from(value: Vec<PrimitiveType>) -> Self {
let mut result = Self::new();
for primitive_type in value {
result |= primitive_type;
}
result
}
}
/// Iterator over all `PrimitiveType` present in a `PrimitiveTypesBitMap`
#[derive(Debug)]
pub struct PrimitiveTypesBitMapIterator {
idx: u8,
bit_map: PrimitiveTypesBitMap,
}
impl Iterator for PrimitiveTypesBitMapIterator {
type Item = PrimitiveType;
#[allow(clippy::arithmetic_side_effects)]
fn next(&mut self) -> Option<Self::Item> {
while self.idx <= 7 {
let bit_value = 1 << self.idx;
self.idx += 1;
if self.bit_map.inner & bit_value != 0 {
return Some(bit_map_representation_primitive_type(bit_value));
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multiple_types() {
let mut types = PrimitiveTypesBitMap::new();
types |= PrimitiveType::Null;
types |= PrimitiveType::String;
types |= PrimitiveType::Array;
assert!(types.contains_type(PrimitiveType::Null));
assert!(types.contains_type(PrimitiveType::String));
assert!(types.contains_type(PrimitiveType::Array));
assert_eq!(
types.into_iter().collect::<Vec<PrimitiveType>>(),
vec![
PrimitiveType::Array,
PrimitiveType::Null,
PrimitiveType::String
]
)
}
}