use std::collections::{BTreeMap, HashMap};
pub type ValueMap = indexmap::IndexMap<String, Value>;
pub trait IntoValueMap {
fn into_value_map(self) -> ValueMap;
}
impl IntoValueMap for HashMap<String, Value> {
fn into_value_map(self) -> ValueMap {
self.into_iter().collect()
}
}
impl IntoValueMap for ValueMap {
fn into_value_map(self) -> ValueMap {
self
}
}
impl IntoValueMap for BTreeMap<String, Value> {
fn into_value_map(self) -> ValueMap {
self.into_iter().collect()
}
}
#[derive(
Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd, serde::Serialize, serde::Deserialize,
)]
pub struct VertexId(pub u64);
pub trait IntoVertexId {
fn into_vertex_id(self) -> VertexId;
}
impl IntoVertexId for VertexId {
#[inline]
fn into_vertex_id(self) -> VertexId {
self
}
}
impl IntoVertexId for &VertexId {
#[inline]
fn into_vertex_id(self) -> VertexId {
*self
}
}
impl IntoVertexId for u64 {
#[inline]
fn into_vertex_id(self) -> VertexId {
VertexId(self)
}
}
#[derive(
Copy, Clone, Eq, PartialEq, Hash, Debug, Ord, PartialOrd, serde::Serialize, serde::Deserialize,
)]
pub struct EdgeId(pub u64);
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum ElementId {
Vertex(VertexId),
Edge(EdgeId),
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Value {
Null,
Bool(bool),
Int(i64),
Float(f64),
String(String),
List(Vec<Value>),
Map(ValueMap),
Vertex(VertexId),
Edge(EdgeId),
Point(crate::geo::Point),
Polygon(crate::geo::Polygon),
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ComparableValue {
Null,
Bool(bool),
Int(i64),
Float(OrderedFloat),
String(String),
List(Vec<ComparableValue>),
Map(BTreeMap<String, ComparableValue>),
Vertex(VertexId),
Edge(EdgeId),
Point(OrderedFloat, OrderedFloat),
Polygon(Vec<(OrderedFloat, OrderedFloat)>),
}
#[derive(Copy, Clone, Debug)]
pub struct OrderedFloat(pub f64);
impl PartialEq for OrderedFloat {
fn eq(&self, other: &Self) -> bool {
self.0.to_bits() == other.0.to_bits()
}
}
impl Eq for OrderedFloat {}
impl PartialOrd for OrderedFloat {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for OrderedFloat {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.total_cmp(&other.0)
}
}
impl std::hash::Hash for OrderedFloat {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.to_bits().hash(state);
}
}
impl std::hash::Hash for Value {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
Value::Null => {}
Value::Bool(b) => b.hash(state),
Value::Int(n) => n.hash(state),
Value::Float(f) => f.to_bits().hash(state),
Value::String(s) => s.hash(state),
Value::List(items) => items.hash(state),
Value::Map(map) => {
let mut entries: Vec<_> = map.iter().collect();
entries.sort_by_key(|(k, _)| *k);
for (k, v) in entries {
k.hash(state);
v.hash(state);
}
}
Value::Vertex(id) => id.hash(state),
Value::Edge(id) => id.hash(state),
Value::Point(p) => {
p.lon.to_bits().hash(state);
p.lat.to_bits().hash(state);
}
Value::Polygon(poly) => {
for &(lon, lat) in &poly.ring {
lon.to_bits().hash(state);
lat.to_bits().hash(state);
}
}
}
}
}
impl Eq for Value {}
impl From<bool> for Value {
fn from(value: bool) -> Self {
Value::Bool(value)
}
}
impl From<i64> for Value {
fn from(value: i64) -> Self {
Value::Int(value)
}
}
impl From<i32> for Value {
fn from(value: i32) -> Self {
Value::Int(value as i64)
}
}
impl From<u64> for Value {
fn from(value: u64) -> Self {
Value::Int(value as i64)
}
}
impl From<u32> for Value {
fn from(value: u32) -> Self {
Value::Int(value as i64)
}
}
impl From<f64> for Value {
fn from(value: f64) -> Self {
Value::Float(value)
}
}
impl From<f32> for Value {
fn from(value: f32) -> Self {
Value::Float(value as f64)
}
}
impl From<String> for Value {
fn from(value: String) -> Self {
Value::String(value)
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Value::String(value.to_owned())
}
}
impl From<Vec<Value>> for Value {
fn from(values: Vec<Value>) -> Self {
Value::List(values)
}
}
impl From<HashMap<String, Value>> for Value {
fn from(map: HashMap<String, Value>) -> Self {
Value::Map(map.into_iter().collect())
}
}
impl From<ValueMap> for Value {
fn from(map: ValueMap) -> Self {
Value::Map(map)
}
}
impl From<VertexId> for Value {
fn from(id: VertexId) -> Self {
Value::Vertex(id)
}
}
impl From<EdgeId> for Value {
fn from(id: EdgeId) -> Self {
Value::Edge(id)
}
}
impl From<crate::geo::Point> for Value {
fn from(p: crate::geo::Point) -> Self {
Value::Point(p)
}
}
impl From<crate::geo::Polygon> for Value {
fn from(p: crate::geo::Polygon) -> Self {
Value::Polygon(p)
}
}
impl Value {
pub fn serialize(&self, buf: &mut Vec<u8>) {
match self {
Value::Null => buf.push(0x00),
Value::Bool(false) => buf.push(0x01),
Value::Bool(true) => buf.push(0x02),
Value::Int(n) => {
buf.push(0x03);
buf.extend_from_slice(&n.to_le_bytes());
}
Value::Float(f) => {
buf.push(0x04);
buf.extend_from_slice(&f.to_le_bytes());
}
Value::String(s) => {
buf.push(0x05);
let len = s.len() as u32;
buf.extend_from_slice(&len.to_le_bytes());
buf.extend_from_slice(s.as_bytes());
}
Value::List(items) => {
buf.push(0x06);
let len = items.len() as u32;
buf.extend_from_slice(&len.to_le_bytes());
for item in items {
item.serialize(buf);
}
}
Value::Map(map) => {
buf.push(0x07);
let len = map.len() as u32;
buf.extend_from_slice(&len.to_le_bytes());
for (k, v) in map {
Value::String(k.clone()).serialize(buf);
v.serialize(buf);
}
}
Value::Vertex(id) => {
buf.push(0x08);
buf.extend_from_slice(&id.0.to_le_bytes());
}
Value::Edge(id) => {
buf.push(0x09);
buf.extend_from_slice(&id.0.to_le_bytes());
}
Value::Point(p) => {
buf.push(0x0A);
buf.extend_from_slice(&p.lon.to_le_bytes());
buf.extend_from_slice(&p.lat.to_le_bytes());
}
Value::Polygon(poly) => {
buf.push(0x0B);
let n = poly.ring.len() as u32;
buf.extend_from_slice(&n.to_le_bytes());
for &(lon, lat) in &poly.ring {
buf.extend_from_slice(&lon.to_le_bytes());
buf.extend_from_slice(&lat.to_le_bytes());
}
}
}
}
pub fn deserialize(buf: &[u8], pos: &mut usize) -> Option<Value> {
let tag = *buf.get(*pos)?;
*pos += 1;
match tag {
0x00 => Some(Value::Null),
0x01 => Some(Value::Bool(false)),
0x02 => Some(Value::Bool(true)),
0x03 => {
let n = i64::from_le_bytes(buf.get(*pos..*pos + 8)?.try_into().ok()?);
*pos += 8;
Some(Value::Int(n))
}
0x04 => {
let f = f64::from_le_bytes(buf.get(*pos..*pos + 8)?.try_into().ok()?);
*pos += 8;
Some(Value::Float(f))
}
0x05 => {
let len = u32::from_le_bytes(buf.get(*pos..*pos + 4)?.try_into().ok()?) as usize;
*pos += 4;
let slice = buf.get(*pos..*pos + len)?;
*pos += len;
let s = std::str::from_utf8(slice).ok()?;
Some(Value::String(s.to_owned()))
}
0x06 => {
let len = u32::from_le_bytes(buf.get(*pos..*pos + 4)?.try_into().ok()?) as usize;
*pos += 4;
let mut items = Vec::with_capacity(len);
for _ in 0..len {
let item = Value::deserialize(buf, pos)?;
items.push(item);
}
Some(Value::List(items))
}
0x07 => {
let len = u32::from_le_bytes(buf.get(*pos..*pos + 4)?.try_into().ok()?) as usize;
*pos += 4;
let mut map = ValueMap::with_capacity(len);
for _ in 0..len {
let key = match Value::deserialize(buf, pos)? {
Value::String(s) => s,
_ => return None,
};
let value = Value::deserialize(buf, pos)?;
map.insert(key, value);
}
Some(Value::Map(map))
}
0x08 => {
let id = u64::from_le_bytes(buf.get(*pos..*pos + 8)?.try_into().ok()?);
*pos += 8;
Some(Value::Vertex(VertexId(id)))
}
0x09 => {
let id = u64::from_le_bytes(buf.get(*pos..*pos + 8)?.try_into().ok()?);
*pos += 8;
Some(Value::Edge(EdgeId(id)))
}
0x0A => {
let lon = f64::from_le_bytes(buf.get(*pos..*pos + 8)?.try_into().ok()?);
*pos += 8;
let lat = f64::from_le_bytes(buf.get(*pos..*pos + 8)?.try_into().ok()?);
*pos += 8;
Some(Value::Point(crate::geo::Point::new_unchecked(lon, lat)))
}
0x0B => {
let n = u32::from_le_bytes(buf.get(*pos..*pos + 4)?.try_into().ok()?) as usize;
*pos += 4;
let mut ring = Vec::with_capacity(n);
for _ in 0..n {
let lon = f64::from_le_bytes(buf.get(*pos..*pos + 8)?.try_into().ok()?);
*pos += 8;
let lat = f64::from_le_bytes(buf.get(*pos..*pos + 8)?.try_into().ok()?);
*pos += 8;
ring.push((lon, lat));
}
Some(Value::Polygon(crate::geo::Polygon { ring }))
}
_ => None,
}
}
pub fn to_comparable(&self) -> ComparableValue {
match self {
Value::Null => ComparableValue::Null,
Value::Bool(b) => ComparableValue::Bool(*b),
Value::Int(n) => ComparableValue::Int(*n),
Value::Float(f) => ComparableValue::Float(OrderedFloat(*f)),
Value::String(s) => ComparableValue::String(s.clone()),
Value::List(items) => {
ComparableValue::List(items.iter().map(Value::to_comparable).collect())
}
Value::Map(map) => {
let mut ordered = BTreeMap::new();
for (k, v) in map {
ordered.insert(k.clone(), v.to_comparable());
}
ComparableValue::Map(ordered)
}
Value::Vertex(id) => ComparableValue::Vertex(*id),
Value::Edge(id) => ComparableValue::Edge(*id),
Value::Point(p) => ComparableValue::Point(OrderedFloat(p.lon), OrderedFloat(p.lat)),
Value::Polygon(poly) => ComparableValue::Polygon(
poly.ring
.iter()
.map(|&(lon, lat)| (OrderedFloat(lon), OrderedFloat(lat)))
.collect(),
),
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Value::Int(n) => Some(*n),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Value::Float(f) => Some(*f),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(s) => Some(s.as_str()),
_ => None,
}
}
pub fn as_list(&self) -> Option<&Vec<Value>> {
match self {
Value::List(items) => Some(items),
_ => None,
}
}
pub fn as_map(&self) -> Option<&ValueMap> {
match self {
Value::Map(map) => Some(map),
_ => None,
}
}
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
#[inline]
pub fn as_vertex_id(&self) -> Option<VertexId> {
match self {
Value::Vertex(id) => Some(*id),
_ => None,
}
}
#[inline]
pub fn as_edge_id(&self) -> Option<EdgeId> {
match self {
Value::Edge(id) => Some(*id),
_ => None,
}
}
#[inline]
pub fn is_vertex(&self) -> bool {
matches!(self, Value::Vertex(_))
}
#[inline]
pub fn is_edge(&self) -> bool {
matches!(self, Value::Edge(_))
}
#[inline]
pub fn as_point(&self) -> Option<crate::geo::Point> {
match self {
Value::Point(p) => Some(*p),
_ => None,
}
}
#[inline]
pub fn as_polygon(&self) -> Option<&crate::geo::Polygon> {
match self {
Value::Polygon(p) => Some(p),
_ => None,
}
}
#[inline]
pub fn is_point(&self) -> bool {
matches!(self, Value::Point(_))
}
#[inline]
pub fn is_polygon(&self) -> bool {
matches!(self, Value::Polygon(_))
}
pub fn discriminant(&self) -> u8 {
match self {
Value::Null => 0x00,
Value::Bool(false) => 0x01,
Value::Bool(true) => 0x02,
Value::Int(_) => 0x03,
Value::Float(_) => 0x04,
Value::String(_) => 0x05,
Value::List(_) => 0x06,
Value::Map(_) => 0x07,
Value::Vertex(_) => 0x08,
Value::Edge(_) => 0x09,
Value::Point(_) => 0x0A,
Value::Polygon(_) => 0x0B,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn converts_primitives_into_value() {
let bool_v: Value = true.into();
let int_v: Value = 42i32.into();
let uint_v: Value = 7u32.into();
let float_v: Value = 3.15f32.into(); let double_v: Value = 6.30f64.into(); let string_v: Value = "hello".into();
assert_eq!(bool_v, Value::Bool(true));
assert_eq!(int_v, Value::Int(42));
assert_eq!(uint_v, Value::Int(7));
assert!(matches!(float_v, Value::Float(v) if (v - 3.15f64).abs() < 1e-6));
assert!(matches!(double_v, Value::Float(v) if (v - 6.30f64).abs() < 1e-12));
assert_eq!(string_v, Value::String("hello".to_string()));
}
#[test]
fn converts_collections_into_value() {
let list_v: Value = vec![Value::Int(1), Value::Bool(false)].into();
let mut map = crate::value::ValueMap::new();
map.insert("a".to_string(), Value::Int(1));
map.insert("b".to_string(), Value::Bool(true));
let map_v: Value = map.clone().into();
assert_eq!(list_v, Value::List(vec![Value::Int(1), Value::Bool(false)]));
assert_eq!(map_v, Value::Map(map));
}
#[test]
fn orders_and_compares_ids() {
let v1 = VertexId(1);
let v2 = VertexId(2);
let e1 = EdgeId(1);
let e2 = EdgeId(2);
assert!(v1 < v2);
assert!(e1 < e2);
assert_eq!(ElementId::Vertex(v1), ElementId::Vertex(VertexId(1)));
assert_eq!(ElementId::Edge(e2), ElementId::Edge(EdgeId(2)));
}
#[test]
fn serializes_and_deserializes_roundtrip() {
let mut original_map = crate::value::ValueMap::new();
original_map.insert("name".to_string(), Value::String("Alice".to_string()));
original_map.insert("age".to_string(), Value::Int(30));
let value = Value::List(vec![
Value::Null,
Value::Bool(true),
Value::Int(-7),
Value::Float(3.5),
Value::String("hello".to_string()),
Value::Map(original_map.clone()),
]);
let mut buf = Vec::new();
value.serialize(&mut buf);
let mut pos = 0;
let parsed = Value::deserialize(&buf, &mut pos).expect("deserialize");
assert_eq!(pos, buf.len());
assert_eq!(parsed, value);
}
#[test]
fn comparable_value_orders_consistently() {
let a = Value::String("a".to_string()).to_comparable();
let b = Value::String("b".to_string()).to_comparable();
assert!(a < b);
let list_small = Value::List(vec![Value::Int(1)]).to_comparable();
let list_large = Value::List(vec![Value::Int(1), Value::Int(2)]).to_comparable();
assert!(list_small < list_large);
}
#[test]
fn as_bool_extracts_boolean_values() {
assert_eq!(Value::Bool(true).as_bool(), Some(true));
assert_eq!(Value::Bool(false).as_bool(), Some(false));
assert_eq!(Value::Int(1).as_bool(), None);
assert_eq!(Value::Null.as_bool(), None);
}
#[test]
fn as_i64_extracts_integer_values() {
assert_eq!(Value::Int(42).as_i64(), Some(42));
assert_eq!(Value::Int(-7).as_i64(), Some(-7));
assert_eq!(Value::Bool(true).as_i64(), None);
assert_eq!(Value::Float(42.0).as_i64(), None);
}
#[test]
fn as_f64_extracts_float_values() {
assert_eq!(Value::Float(3.15).as_f64(), Some(3.15));
assert_eq!(Value::Float(-2.5).as_f64(), Some(-2.5));
assert_eq!(Value::Int(42).as_f64(), None);
assert_eq!(Value::Null.as_f64(), None);
}
#[test]
fn as_str_extracts_string_values() {
let s = Value::String("hello".to_string());
assert_eq!(s.as_str(), Some("hello"));
assert_eq!(Value::Int(42).as_str(), None);
assert_eq!(Value::Null.as_str(), None);
}
#[test]
fn as_list_extracts_list_values() {
let list = Value::List(vec![Value::Int(1), Value::Bool(true)]);
let extracted = list.as_list();
assert!(extracted.is_some());
assert_eq!(extracted.unwrap().len(), 2);
assert_eq!(extracted.unwrap()[0], Value::Int(1));
assert_eq!(Value::Null.as_list(), None);
assert_eq!(Value::Int(42).as_list(), None);
}
#[test]
fn as_map_extracts_map_values() {
let mut map = crate::value::ValueMap::new();
map.insert("key".to_string(), Value::Int(42));
let value = Value::Map(map.clone());
let extracted = value.as_map();
assert!(extracted.is_some());
assert_eq!(extracted.unwrap().get("key"), Some(&Value::Int(42)));
assert_eq!(Value::Null.as_map(), None);
assert_eq!(Value::String("test".to_string()).as_map(), None);
}
#[test]
fn is_null_detects_null_values() {
assert!(Value::Null.is_null());
assert!(!Value::Bool(false).is_null());
assert!(!Value::Int(0).is_null());
assert!(!Value::String("".to_string()).is_null());
}
#[test]
fn accessor_methods_work_with_conversions() {
let v: Value = 42i32.into();
assert_eq!(v.as_i64(), Some(42));
let s: Value = "test".into();
assert_eq!(s.as_str(), Some("test"));
let b: Value = true.into();
assert_eq!(b.as_bool(), Some(true));
let f: Value = 3.15f64.into();
assert_eq!(f.as_f64(), Some(3.15));
}
#[test]
fn value_vertex_variant_compiles_and_pattern_matches() {
let v = Value::Vertex(VertexId(1));
assert!(matches!(v, Value::Vertex(VertexId(1))));
let v2 = Value::Vertex(VertexId(42));
match v2 {
Value::Vertex(id) => assert_eq!(id, VertexId(42)),
_ => panic!("Expected Vertex variant"),
}
}
#[test]
fn value_edge_variant_compiles_and_pattern_matches() {
let e = Value::Edge(EdgeId(1));
assert!(matches!(e, Value::Edge(EdgeId(1))));
let e2 = Value::Edge(EdgeId(99));
match e2 {
Value::Edge(id) => assert_eq!(id, EdgeId(99)),
_ => panic!("Expected Edge variant"),
}
}
#[test]
fn vertex_and_edge_to_comparable() {
let v = Value::Vertex(VertexId(123));
let cv = v.to_comparable();
assert_eq!(cv, ComparableValue::Vertex(VertexId(123)));
let e = Value::Edge(EdgeId(456));
let ce = e.to_comparable();
assert_eq!(ce, ComparableValue::Edge(EdgeId(456)));
}
#[test]
fn vertex_and_edge_serialize_roundtrip() {
let vertex = Value::Vertex(VertexId(12345));
let mut buf = Vec::new();
vertex.serialize(&mut buf);
assert_eq!(buf[0], 0x08); let mut pos = 0;
let parsed = Value::deserialize(&buf, &mut pos).expect("deserialize vertex");
assert_eq!(parsed, vertex);
assert_eq!(pos, buf.len());
let edge = Value::Edge(EdgeId(67890));
let mut buf = Vec::new();
edge.serialize(&mut buf);
assert_eq!(buf[0], 0x09); let mut pos = 0;
let parsed = Value::deserialize(&buf, &mut pos).expect("deserialize edge");
assert_eq!(parsed, edge);
assert_eq!(pos, buf.len());
}
#[test]
fn value_can_be_used_as_hashmap_key() {
use std::collections::HashMap;
let mut map: HashMap<Value, i32> = HashMap::new();
map.insert(Value::Int(42), 1);
map.insert(Value::String("hello".to_string()), 2);
map.insert(Value::Bool(true), 3);
map.insert(Value::Null, 4);
map.insert(Value::Float(3.15), 5);
map.insert(Value::Vertex(VertexId(100)), 6);
map.insert(Value::Edge(EdgeId(200)), 7);
map.insert(Value::List(vec![Value::Int(1), Value::Int(2)]), 8);
assert_eq!(map.get(&Value::Int(42)), Some(&1));
assert_eq!(map.get(&Value::String("hello".to_string())), Some(&2));
assert_eq!(map.get(&Value::Bool(true)), Some(&3));
assert_eq!(map.get(&Value::Null), Some(&4));
assert_eq!(map.get(&Value::Float(3.15)), Some(&5));
assert_eq!(map.get(&Value::Vertex(VertexId(100))), Some(&6));
assert_eq!(map.get(&Value::Edge(EdgeId(200))), Some(&7));
assert_eq!(
map.get(&Value::List(vec![Value::Int(1), Value::Int(2)])),
Some(&8)
);
}
#[test]
fn value_can_be_inserted_into_hashset() {
use std::collections::HashSet;
let mut set: HashSet<Value> = HashSet::new();
set.insert(Value::Int(1));
set.insert(Value::Int(2));
set.insert(Value::Int(1)); set.insert(Value::String("test".to_string()));
set.insert(Value::Vertex(VertexId(42)));
set.insert(Value::Edge(EdgeId(99)));
assert_eq!(set.len(), 5); assert!(set.contains(&Value::Int(1)));
assert!(set.contains(&Value::Int(2)));
assert!(set.contains(&Value::String("test".to_string())));
assert!(set.contains(&Value::Vertex(VertexId(42))));
assert!(set.contains(&Value::Edge(EdgeId(99))));
}
#[test]
fn value_hash_is_consistent() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn hash_value(v: &Value) -> u64 {
let mut hasher = DefaultHasher::new();
v.hash(&mut hasher);
hasher.finish()
}
assert_eq!(hash_value(&Value::Int(42)), hash_value(&Value::Int(42)));
assert_eq!(
hash_value(&Value::String("hello".to_string())),
hash_value(&Value::String("hello".to_string()))
);
assert_eq!(
hash_value(&Value::Float(3.15)),
hash_value(&Value::Float(3.15))
);
assert_eq!(hash_value(&Value::Null), hash_value(&Value::Null));
assert_eq!(
hash_value(&Value::Bool(true)),
hash_value(&Value::Bool(true))
);
assert_eq!(
hash_value(&Value::Vertex(VertexId(100))),
hash_value(&Value::Vertex(VertexId(100)))
);
assert_eq!(
hash_value(&Value::Edge(EdgeId(200))),
hash_value(&Value::Edge(EdgeId(200)))
);
assert_ne!(hash_value(&Value::Int(1)), hash_value(&Value::Int(2)));
assert_ne!(
hash_value(&Value::String("a".to_string())),
hash_value(&Value::String("b".to_string()))
);
assert_ne!(hash_value(&Value::Int(42)), hash_value(&Value::Float(42.0)));
assert_ne!(
hash_value(&Value::Vertex(VertexId(1))),
hash_value(&Value::Edge(EdgeId(1)))
);
}
#[test]
fn value_map_hash_is_order_independent() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn hash_value(v: &Value) -> u64 {
let mut hasher = DefaultHasher::new();
v.hash(&mut hasher);
hasher.finish()
}
let mut map1 = crate::value::ValueMap::new();
map1.insert("a".to_string(), Value::Int(1));
map1.insert("b".to_string(), Value::Int(2));
map1.insert("c".to_string(), Value::Int(3));
let mut map2 = crate::value::ValueMap::new();
map2.insert("c".to_string(), Value::Int(3));
map2.insert("a".to_string(), Value::Int(1));
map2.insert("b".to_string(), Value::Int(2));
let v1 = Value::Map(map1);
let v2 = Value::Map(map2);
assert_eq!(hash_value(&v1), hash_value(&v2));
assert_eq!(v1, v2);
}
#[test]
fn from_vertex_id_for_value() {
let id = VertexId(42);
let v: Value = id.into();
assert_eq!(v, Value::Vertex(VertexId(42)));
let v2 = Value::from(VertexId(123));
assert_eq!(v2, Value::Vertex(VertexId(123)));
}
#[test]
fn from_edge_id_for_value() {
let id = EdgeId(99);
let v: Value = id.into();
assert_eq!(v, Value::Edge(EdgeId(99)));
let v2 = Value::from(EdgeId(456));
assert_eq!(v2, Value::Edge(EdgeId(456)));
}
#[test]
fn as_vertex_id_extracts_vertex_id() {
let v = Value::Vertex(VertexId(42));
assert_eq!(v.as_vertex_id(), Some(VertexId(42)));
assert_eq!(Value::Int(42).as_vertex_id(), None);
assert_eq!(Value::Edge(EdgeId(42)).as_vertex_id(), None);
assert_eq!(Value::Null.as_vertex_id(), None);
assert_eq!(Value::String("vertex".to_string()).as_vertex_id(), None);
}
#[test]
fn as_edge_id_extracts_edge_id() {
let e = Value::Edge(EdgeId(99));
assert_eq!(e.as_edge_id(), Some(EdgeId(99)));
assert_eq!(Value::Int(99).as_edge_id(), None);
assert_eq!(Value::Vertex(VertexId(99)).as_edge_id(), None);
assert_eq!(Value::Null.as_edge_id(), None);
assert_eq!(Value::String("edge".to_string()).as_edge_id(), None);
}
#[test]
fn is_vertex_detects_vertex_values() {
assert!(Value::Vertex(VertexId(1)).is_vertex());
assert!(Value::Vertex(VertexId(0)).is_vertex());
assert!(Value::Vertex(VertexId(u64::MAX)).is_vertex());
assert!(!Value::Edge(EdgeId(1)).is_vertex());
assert!(!Value::Int(1).is_vertex());
assert!(!Value::Null.is_vertex());
assert!(!Value::Bool(true).is_vertex());
assert!(!Value::String("vertex".to_string()).is_vertex());
}
#[test]
fn is_edge_detects_edge_values() {
assert!(Value::Edge(EdgeId(1)).is_edge());
assert!(Value::Edge(EdgeId(0)).is_edge());
assert!(Value::Edge(EdgeId(u64::MAX)).is_edge());
assert!(!Value::Vertex(VertexId(1)).is_edge());
assert!(!Value::Int(1).is_edge());
assert!(!Value::Null.is_edge());
assert!(!Value::Bool(true).is_edge());
assert!(!Value::String("edge".to_string()).is_edge());
}
fn arb_value() -> impl Strategy<Value = Value> {
let leaf = prop_oneof![
Just(Value::Null),
any::<bool>().prop_map(Value::Bool),
any::<i64>().prop_map(Value::Int),
any::<f64>().prop_map(Value::Float),
"[a-zA-Z0-9]{0,8}".prop_map(Value::String),
any::<u64>().prop_map(|n| Value::Vertex(VertexId(n))),
any::<u64>().prop_map(|n| Value::Edge(EdgeId(n))),
];
leaf.prop_recursive(4, 64, 8, |inner| {
let list = prop::collection::vec(inner.clone(), 0..4).prop_map(Value::List);
let map = prop::collection::hash_map("[a-zA-Z0-9]{0,6}", inner, 0..4)
.prop_map(|m| Value::Map(m.into_iter().collect()));
prop_oneof![list, map]
})
}
#[test]
fn discriminant_matches_serialization_tag() {
assert_eq!(Value::Null.discriminant(), 0x00);
let mut buf = Vec::new();
Value::Null.serialize(&mut buf);
assert_eq!(buf[0], 0x00);
assert_eq!(Value::Bool(false).discriminant(), 0x01);
let mut buf = Vec::new();
Value::Bool(false).serialize(&mut buf);
assert_eq!(buf[0], 0x01);
assert_eq!(Value::Bool(true).discriminant(), 0x02);
let mut buf = Vec::new();
Value::Bool(true).serialize(&mut buf);
assert_eq!(buf[0], 0x02);
assert_eq!(Value::Int(42).discriminant(), 0x03);
let mut buf = Vec::new();
Value::Int(42).serialize(&mut buf);
assert_eq!(buf[0], 0x03);
assert_eq!(Value::Float(3.15).discriminant(), 0x04);
let mut buf = Vec::new();
Value::Float(3.15).serialize(&mut buf);
assert_eq!(buf[0], 0x04);
assert_eq!(Value::String("test".to_string()).discriminant(), 0x05);
let mut buf = Vec::new();
Value::String("test".to_string()).serialize(&mut buf);
assert_eq!(buf[0], 0x05);
assert_eq!(Value::List(vec![]).discriminant(), 0x06);
let mut buf = Vec::new();
Value::List(vec![]).serialize(&mut buf);
assert_eq!(buf[0], 0x06);
assert_eq!(
Value::Map(crate::value::ValueMap::new()).discriminant(),
0x07
);
let mut buf = Vec::new();
Value::Map(crate::value::ValueMap::new()).serialize(&mut buf);
assert_eq!(buf[0], 0x07);
assert_eq!(Value::Vertex(VertexId(1)).discriminant(), 0x08);
let mut buf = Vec::new();
Value::Vertex(VertexId(1)).serialize(&mut buf);
assert_eq!(buf[0], 0x08);
assert_eq!(Value::Edge(EdgeId(1)).discriminant(), 0x09);
let mut buf = Vec::new();
Value::Edge(EdgeId(1)).serialize(&mut buf);
assert_eq!(buf[0], 0x09);
}
#[test]
fn discriminant_is_unique_for_each_type() {
let values = vec![
Value::Null,
Value::Bool(false),
Value::Bool(true),
Value::Int(0),
Value::Float(0.0),
Value::String("".to_string()),
Value::List(vec![]),
Value::Map(crate::value::ValueMap::new()),
Value::Vertex(VertexId(0)),
Value::Edge(EdgeId(0)),
];
let discriminants: Vec<u8> = values.iter().map(|v| v.discriminant()).collect();
for i in 0..discriminants.len() {
for j in (i + 1)..discriminants.len() {
assert_ne!(
discriminants[i], discriminants[j],
"Discriminants not unique: {} vs {}",
discriminants[i], discriminants[j]
);
}
}
}
#[test]
fn complex_value_serialization_roundtrip() {
let mut map = crate::value::ValueMap::new();
map.insert("null".to_string(), Value::Null);
map.insert("bool".to_string(), Value::Bool(true));
map.insert("int".to_string(), Value::Int(-42));
map.insert("float".to_string(), Value::Float(2.72)); map.insert("string".to_string(), Value::String("nested".to_string()));
map.insert(
"list".to_string(),
Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
);
map.insert("vertex".to_string(), Value::Vertex(VertexId(100)));
map.insert("edge".to_string(), Value::Edge(EdgeId(200)));
let complex = Value::Map(map);
let mut buf = Vec::new();
complex.serialize(&mut buf);
let mut pos = 0;
let parsed = Value::deserialize(&buf, &mut pos).expect("deserialize");
assert_eq!(parsed, complex);
assert_eq!(pos, buf.len());
}
#[test]
fn deeply_nested_list_roundtrip() {
let inner = Value::List(vec![Value::Int(1), Value::Int(2)]);
let middle = Value::List(vec![inner.clone(), inner.clone()]);
let outer = Value::List(vec![middle.clone(), middle]);
let mut buf = Vec::new();
outer.serialize(&mut buf);
let mut pos = 0;
let parsed = Value::deserialize(&buf, &mut pos).expect("deserialize");
assert_eq!(parsed, outer);
assert_eq!(pos, buf.len());
}
#[test]
fn empty_collections_roundtrip() {
let empty_list = Value::List(vec![]);
let mut buf = Vec::new();
empty_list.serialize(&mut buf);
let mut pos = 0;
let parsed = Value::deserialize(&buf, &mut pos).expect("deserialize");
assert_eq!(parsed, empty_list);
let empty_map = Value::Map(crate::value::ValueMap::new());
let mut buf = Vec::new();
empty_map.serialize(&mut buf);
let mut pos = 0;
let parsed = Value::deserialize(&buf, &mut pos).expect("deserialize");
assert_eq!(parsed, empty_map);
}
#[test]
fn large_string_roundtrip() {
let large_string = "a".repeat(1000);
let value = Value::String(large_string.clone());
let mut buf = Vec::new();
value.serialize(&mut buf);
let mut pos = 0;
let parsed = Value::deserialize(&buf, &mut pos).expect("deserialize");
assert_eq!(parsed, value);
assert_eq!(parsed.as_str(), Some(large_string.as_str()));
}
#[test]
fn special_float_values_roundtrip() {
let values = vec![
Value::Float(f64::NAN),
Value::Float(f64::INFINITY),
Value::Float(f64::NEG_INFINITY),
Value::Float(-0.0),
Value::Float(0.0),
];
for val in values {
let mut buf = Vec::new();
val.serialize(&mut buf);
let mut pos = 0;
let parsed = Value::deserialize(&buf, &mut pos).expect("deserialize");
match (&val, &parsed) {
(Value::Float(f1), Value::Float(f2)) if f1.is_nan() => {
assert!(f2.is_nan(), "NaN did not roundtrip correctly");
}
_ => {
assert_eq!(parsed, val, "Value did not roundtrip correctly");
}
}
}
}
proptest! {
#[test]
fn value_roundtrips_through_serialization(v in arb_value()) {
let mut buf = Vec::new();
v.serialize(&mut buf);
let mut pos = 0;
let parsed = Value::deserialize(&buf, &mut pos).expect("deserialize");
prop_assert_eq!(parsed, v);
prop_assert_eq!(pos, buf.len());
}
#[test]
fn discriminant_matches_first_byte_of_serialization(v in arb_value()) {
let mut buf = Vec::new();
v.serialize(&mut buf);
prop_assert!(!buf.is_empty());
prop_assert_eq!(buf[0], v.discriminant());
}
}
#[test]
fn into_vertex_id_from_vertex_id() {
use super::IntoVertexId;
let id = VertexId(42);
assert_eq!(id.into_vertex_id(), VertexId(42));
}
#[test]
fn into_vertex_id_from_ref_vertex_id() {
use super::IntoVertexId;
let id = VertexId(42);
assert_eq!((&id).into_vertex_id(), VertexId(42));
}
#[test]
fn into_vertex_id_from_u64() {
use super::IntoVertexId;
assert_eq!(42u64.into_vertex_id(), VertexId(42));
assert_eq!(0u64.into_vertex_id(), VertexId(0));
assert_eq!(u64::MAX.into_vertex_id(), VertexId(u64::MAX));
}
}