1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! 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::integer_arithmetic)]
    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
            ]
        )
    }
}