use alloc::vec::Vec;
use core::fmt;
use serde_json::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum JsonType {
Null,
Boolean,
Integer,
Number,
String,
Array,
Object,
}
impl JsonType {
#[must_use]
pub fn of(value: &Value) -> Self {
match value {
Value::Null => Self::Null,
Value::Bool(_) => Self::Boolean,
Value::Number(n) => {
if n.is_i64() || n.is_u64() {
Self::Integer
} else if let Some(f) = n.as_f64() {
if f.fract() == 0.0 {
Self::Integer
} else {
Self::Number
}
} else {
Self::Number
}
}
Value::String(_) => Self::String,
Value::Array(_) => Self::Array,
Value::Object(_) => Self::Object,
}
}
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Null => "null",
Self::Boolean => "boolean",
Self::Integer => "integer",
Self::Number => "number",
Self::String => "string",
Self::Array => "array",
Self::Object => "object",
}
}
}
impl fmt::Display for JsonType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct JsonTypeSet(u8);
impl JsonTypeSet {
#[must_use]
pub const fn new() -> Self {
Self(0)
}
#[must_use]
pub const fn with(ty: JsonType) -> Self {
Self(1 << (ty as u8))
}
pub fn insert(&mut self, ty: JsonType) {
self.0 |= 1 << (ty as u8);
}
#[must_use]
pub fn contains(&self, ty: JsonType) -> bool {
(self.0 & (1 << (ty as u8))) != 0
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0 == 0
}
#[must_use]
pub fn len(&self) -> usize {
self.0.count_ones() as usize
}
#[must_use]
pub fn iter(&self) -> JsonTypeSetIter {
JsonTypeSetIter {
set: self.0,
pos: 0,
}
}
#[must_use]
pub fn from_schema_value(value: &Value) -> Option<Self> {
match value {
Value::String(s) => parse_type_name(s).map(JsonTypeSet::with),
Value::Array(items) => {
let mut set = Self::new();
for item in items {
if let Value::String(s) = item {
set.insert(parse_type_name(s)?);
} else {
return None;
}
}
Some(set)
}
_ => None,
}
}
}
impl Default for JsonTypeSet {
fn default() -> Self {
Self::new()
}
}
impl IntoIterator for &JsonTypeSet {
type Item = JsonType;
type IntoIter = JsonTypeSetIter;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl fmt::Display for JsonTypeSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let types: Vec<&str> = self.iter().map(|t| t.as_str()).collect();
match types.len() {
0 => write!(f, "(empty)"),
1 => write!(f, "{}", types[0]),
_ => write!(f, "({})", types.join(", ")),
}
}
}
pub struct JsonTypeSetIter {
set: u8,
pos: u8,
}
impl Iterator for JsonTypeSetIter {
type Item = JsonType;
fn next(&mut self) -> Option<Self::Item> {
while self.pos < 7 {
let bit = 1u8 << self.pos;
self.pos += 1;
if (self.set & bit) != 0 {
return match self.pos {
1 => Some(JsonType::Null),
2 => Some(JsonType::Boolean),
3 => Some(JsonType::Integer),
4 => Some(JsonType::Number),
5 => Some(JsonType::String),
6 => Some(JsonType::Array),
7 => Some(JsonType::Object),
_ => None,
};
}
}
None
}
}
fn parse_type_name(s: &str) -> Option<JsonType> {
match s {
"null" => Some(JsonType::Null),
"boolean" => Some(JsonType::Boolean),
"integer" => Some(JsonType::Integer),
"number" => Some(JsonType::Number),
"string" => Some(JsonType::String),
"array" => Some(JsonType::Array),
"object" => Some(JsonType::Object),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_json_type_of_null() {
assert_eq!(JsonType::of(&Value::Null), JsonType::Null);
}
#[test]
fn test_json_type_of_boolean() {
assert_eq!(JsonType::of(&Value::Bool(true)), JsonType::Boolean);
assert_eq!(JsonType::of(&Value::Bool(false)), JsonType::Boolean);
}
#[test]
fn test_json_type_of_integer() {
assert_eq!(JsonType::of(&Value::Number(42.into())), JsonType::Integer);
}
#[test]
fn test_json_type_of_number() {
let n: Value = serde_json::Number::from_f64(std::f64::consts::PI)
.unwrap()
.into();
assert_eq!(JsonType::of(&n), JsonType::Number);
}
#[test]
fn test_json_type_of_string() {
assert_eq!(
JsonType::of(&Value::String("hello".into())),
JsonType::String
);
}
#[test]
fn test_json_type_of_array() {
assert_eq!(JsonType::of(&Value::Array(vec![])), JsonType::Array);
}
#[test]
fn test_json_type_of_object() {
assert_eq!(
JsonType::of(&Value::Object(Default::default())),
JsonType::Object
);
}
#[test]
fn test_json_type_set_insert_and_contains() {
let mut set = JsonTypeSet::new();
assert!(set.is_empty());
set.insert(JsonType::String);
set.insert(JsonType::Integer);
assert_eq!(set.len(), 2);
assert!(set.contains(JsonType::String));
assert!(set.contains(JsonType::Integer));
assert!(!set.contains(JsonType::Number));
}
#[test]
fn test_json_type_set_with() {
let set = JsonTypeSet::with(JsonType::Boolean);
assert_eq!(set.len(), 1);
assert!(set.contains(JsonType::Boolean));
}
#[test]
fn test_json_type_set_iter() {
let mut set = JsonTypeSet::new();
set.insert(JsonType::Null);
set.insert(JsonType::String);
let types: Vec<JsonType> = set.iter().collect();
assert_eq!(types, vec![JsonType::Null, JsonType::String]);
}
#[test]
fn test_json_type_set_from_schema_value_string() {
let set = JsonTypeSet::from_schema_value(&Value::String("string".into())).unwrap();
assert!(set.contains(JsonType::String));
}
#[test]
fn test_json_type_set_from_schema_value_array() {
let value = Value::Array(vec![
Value::String("string".into()),
Value::String("number".into()),
]);
let set = JsonTypeSet::from_schema_value(&value).unwrap();
assert!(set.contains(JsonType::String));
assert!(set.contains(JsonType::Number));
assert_eq!(set.len(), 2);
}
#[test]
fn test_json_type_set_from_schema_value_invalid() {
assert!(JsonTypeSet::from_schema_value(&Value::Number(42.into())).is_none());
}
#[test]
fn test_json_type_display() {
assert_eq!(format!("{}", JsonType::String), "string");
assert_eq!(format!("{}", JsonType::Number), "number");
}
#[test]
fn test_json_type_set_display() {
let mut set = JsonTypeSet::new();
set.insert(JsonType::Null);
set.insert(JsonType::String);
assert_eq!(format!("{set}"), "(null, string)");
}
}