pub type Map = indexmap::IndexMap<String, Value>;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
Datetime(String),
Array(Vec<Value>),
Object(Map),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Number {
I64(i64),
U64(u64),
F64(f64),
}
impl Number {
pub fn from_u64(u: u64) -> Self {
match i64::try_from(u) {
Ok(i) => Self::I64(i),
Err(_) => Self::U64(u),
}
}
}
impl From<i64> for Number {
fn from(i: i64) -> Self {
Self::I64(i)
}
}
impl From<u64> for Number {
fn from(u: u64) -> Self {
Self::from_u64(u)
}
}
impl From<f64> for Number {
fn from(f: f64) -> Self {
Self::F64(f)
}
}
impl Value {
pub fn kind(&self) -> &'static str {
match self {
Self::Object(_) => "object",
Self::Array(_) => "array",
Self::String(_) => "string",
Self::Datetime(_) => "datetime",
Self::Number(_) => "number",
Self::Bool(_) => "bool",
Self::Null => "null",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn u64_that_fits_normalises_to_i64() {
assert_eq!(Number::from_u64(1), Number::I64(1));
assert_eq!(Number::from_u64(i64::MAX as u64), Number::I64(i64::MAX));
assert_eq!(Number::from_u64(u64::MAX), Number::U64(u64::MAX));
}
#[test]
fn int_and_float_share_a_kind_but_datetime_does_not() {
assert_eq!(Value::Number(Number::I64(1)).kind(), "number");
assert_eq!(Value::Number(Number::F64(1.5)).kind(), "number");
assert_eq!(Value::String("x".into()).kind(), "string");
assert_eq!(
Value::Datetime("1979-05-27T07:32:00Z".into()).kind(),
"datetime"
);
}
}