use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Value {
Null,
Bool(bool),
Int(i64),
Float(f64),
String(String),
List(Vec<Value>),
Map(HashMap<String, Value>),
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
use std::cmp::Ordering::*;
match (self, other) {
(Value::Null, Value::Null) => Some(Equal),
(Value::Bool(a), Value::Bool(b)) => a.partial_cmp(b),
(Value::Int(a), Value::Int(b)) => a.partial_cmp(b),
(Value::Float(a), Value::Float(b)) => a.partial_cmp(b),
(Value::String(a), Value::String(b)) => a.partial_cmp(b),
(Value::List(a), Value::List(b)) => a.partial_cmp(b),
_ => None,
}
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Null => write!(f, "null"),
Value::Bool(b) => write!(f, "{b}"),
Value::Int(i) => write!(f, "{i}"),
Value::Float(v) => write!(f, "{v}"),
Value::String(s) => write!(f, "{s:?}"),
Value::List(items) => {
write!(f, "[")?;
for (i, item) in items.iter().enumerate() {
if i > 0 { write!(f, ", ")?; }
write!(f, "{item}")?;
}
write!(f, "]")
}
Value::Map(m) => {
write!(f, "{{")?;
for (i, (k, v)) in m.iter().enumerate() {
if i > 0 { write!(f, ", ")?; }
write!(f, "{k}: {v}")?;
}
write!(f, "}}")
}
}
}
}
pub type Properties = HashMap<String, Value>;
pub fn value_index_key(v: &Value) -> Option<String> {
match v {
Value::Null => Some("N:".to_string()),
Value::Bool(b) => Some(if *b { "B:1".to_string() } else { "B:0".to_string() }),
Value::Int(i) => {
let ordered = (*i as u64) ^ 0x8000_0000_0000_0000u64;
Some(format!("I:{ordered:016x}"))
}
Value::Float(f) => {
if f.is_nan() {
return None; }
let bits = f.to_bits();
let ordered = if bits >> 63 == 0 {
bits ^ 0x8000_0000_0000_0000u64 } else {
!bits };
Some(format!("F:{ordered:016x}"))
}
Value::String(s) => Some(format!("S:{s}")),
Value::List(_) | Value::Map(_) => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn key(v: Value) -> String {
value_index_key(&v).unwrap()
}
#[test]
fn int_encoding_is_order_preserving() {
let vals = [i64::MIN, -1000, -1, 0, 1, 1000, i64::MAX];
let encoded: Vec<String> = vals.iter().map(|&i| key(Value::Int(i))).collect();
for w in encoded.windows(2) {
assert!(w[0] < w[1], "expected {} < {}", w[0], w[1]);
}
}
#[test]
fn float_encoding_is_order_preserving() {
let vals: Vec<f64> = vec![f64::NEG_INFINITY, -1e10, -1.0, -0.0, 0.0, 1.0, 1e10, f64::INFINITY];
let encoded: Vec<String> = vals.iter().map(|&f| key(Value::Float(f))).collect();
for w in encoded.windows(2) {
assert!(w[0] <= w[1], "expected {} <= {}", w[0], w[1]);
}
}
#[test]
fn nan_float_is_not_indexable() {
assert!(value_index_key(&Value::Float(f64::NAN)).is_none());
}
#[test]
fn list_is_not_indexable() {
assert!(value_index_key(&Value::List(vec![])).is_none());
}
#[test]
fn type_prefixes_separate_types() {
let ki = key(Value::Int(0));
let kf = key(Value::Float(0.0));
let ks = key(Value::String("0".into()));
assert_ne!(ki, kf);
assert_ne!(ki, ks);
assert_ne!(kf, ks);
}
#[test]
fn bool_order() {
assert!(key(Value::Bool(false)) < key(Value::Bool(true)));
}
}