#![doc(html_root_url = "https://docs.rs/serde-value/0.7.0/")]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use core::cmp::Ordering;
use core::fmt::{Display, Formatter};
use core::hash::{Hash, Hasher};
use cu29_clock::{CuDuration, CuTime};
use ordered_float::OrderedFloat;
use serde::Deserialize;
mod bdec;
mod benc;
mod de;
#[cfg(feature = "python")]
mod python;
mod ser;
pub use de::*;
#[cfg(feature = "python")]
pub use python::{py_to_value, value_to_py};
pub use ser::*;
#[derive(Clone, Debug)]
pub enum Value {
Bool(bool),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
F32(f32),
F64(f64),
Char(char),
String(String),
Unit,
Option(Option<Box<Value>>),
Newtype(Box<Value>),
Seq(Vec<Value>),
Map(BTreeMap<Value, Value>),
Bytes(Vec<u8>),
CuTime(CuTime),
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Value::Bool(v) => write!(f, "{v}"),
Value::U8(v) => write!(f, "{v}"),
Value::U16(v) => write!(f, "{v}"),
Value::U32(v) => write!(f, "{v}"),
Value::U64(v) => write!(f, "{v}"),
Value::U128(v) => write!(f, "{v}"),
Value::I8(v) => write!(f, "{v}"),
Value::I16(v) => write!(f, "{v}"),
Value::I32(v) => write!(f, "{v}"),
Value::I64(v) => write!(f, "{v}"),
Value::I128(v) => write!(f, "{v}"),
Value::F32(v) => write!(f, "{v}"),
Value::F64(v) => write!(f, "{v}"),
Value::Char(v) => write!(f, "{v}"),
Value::String(v) => write!(f, "{v}"),
Value::Unit => write!(f, "()"),
Value::Option(v) => match v {
Some(v) => write!(f, "Some({v})"),
None => write!(f, "None"),
},
Value::Newtype(v) => write!(f, "{v}"),
Value::Seq(v) => {
write!(f, "[")?;
for (i, v) in v.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{v}")?;
}
write!(f, "]")
}
Value::Map(v) => {
write!(f, "{{")?;
for (i, (k, v)) in v.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{k}: {v}")?;
}
write!(f, "}}")
}
Value::Bytes(v) => {
write!(f, "[")?;
for (i, b) in v.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{b:02x}")?;
}
write!(f, "]")
}
Value::CuTime(v) => write!(f, "{v}"),
}
}
}
impl Hash for Value {
fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
self.discriminant().hash(hasher);
match *self {
Value::Bool(v) => v.hash(hasher),
Value::U8(v) => v.hash(hasher),
Value::U16(v) => v.hash(hasher),
Value::U32(v) => v.hash(hasher),
Value::U64(v) => v.hash(hasher),
Value::U128(v) => v.hash(hasher),
Value::I8(v) => v.hash(hasher),
Value::I16(v) => v.hash(hasher),
Value::I32(v) => v.hash(hasher),
Value::I64(v) => v.hash(hasher),
Value::I128(v) => v.hash(hasher),
Value::F32(v) => OrderedFloat(v).hash(hasher),
Value::F64(v) => OrderedFloat(v).hash(hasher),
Value::Char(v) => v.hash(hasher),
Value::String(ref v) => v.hash(hasher),
Value::Unit => 0_u8.hash(hasher),
Value::Option(ref v) => v.hash(hasher),
Value::Newtype(ref v) => v.hash(hasher),
Value::Seq(ref v) => v.hash(hasher),
Value::Map(ref v) => v.hash(hasher),
Value::Bytes(ref v) => v.hash(hasher),
Value::CuTime(v) => {
let CuDuration(nanos) = v;
nanos.hash(hasher)
}
}
}
}
impl PartialEq for Value {
fn eq(&self, rhs: &Self) -> bool {
match (self, rhs) {
(&Value::Bool(v0), &Value::Bool(v1)) if v0 == v1 => true,
(&Value::U8(v0), &Value::U8(v1)) if v0 == v1 => true,
(&Value::U16(v0), &Value::U16(v1)) if v0 == v1 => true,
(&Value::U32(v0), &Value::U32(v1)) if v0 == v1 => true,
(&Value::U64(v0), &Value::U64(v1)) if v0 == v1 => true,
(&Value::U128(v0), &Value::U128(v1)) if v0 == v1 => true,
(&Value::I8(v0), &Value::I8(v1)) if v0 == v1 => true,
(&Value::I16(v0), &Value::I16(v1)) if v0 == v1 => true,
(&Value::I32(v0), &Value::I32(v1)) if v0 == v1 => true,
(&Value::I64(v0), &Value::I64(v1)) if v0 == v1 => true,
(&Value::I128(v0), &Value::I128(v1)) if v0 == v1 => true,
(&Value::F32(v0), &Value::F32(v1)) if OrderedFloat(v0) == OrderedFloat(v1) => true,
(&Value::F64(v0), &Value::F64(v1)) if OrderedFloat(v0) == OrderedFloat(v1) => true,
(&Value::Char(v0), &Value::Char(v1)) if v0 == v1 => true,
(Value::String(v0), Value::String(v1)) if v0 == v1 => true,
(&Value::Unit, &Value::Unit) => true,
(Value::Option(v0), Value::Option(v1)) if v0 == v1 => true,
(Value::Newtype(v0), Value::Newtype(v1)) if v0 == v1 => true,
(Value::Seq(v0), Value::Seq(v1)) if v0 == v1 => true,
(Value::Map(v0), Value::Map(v1)) if v0 == v1 => true,
(Value::Bytes(v0), Value::Bytes(v1)) if v0 == v1 => true,
(&Value::CuTime(v0), &Value::CuTime(v1)) if v0 == v1 => true,
_ => false,
}
}
}
impl Ord for Value {
fn cmp(&self, rhs: &Self) -> Ordering {
match (self, rhs) {
(&Value::Bool(v0), Value::Bool(v1)) => v0.cmp(v1),
(&Value::U8(v0), Value::U8(v1)) => v0.cmp(v1),
(&Value::U16(v0), Value::U16(v1)) => v0.cmp(v1),
(&Value::U32(v0), Value::U32(v1)) => v0.cmp(v1),
(&Value::U64(v0), Value::U64(v1)) => v0.cmp(v1),
(&Value::U128(v0), Value::U128(v1)) => v0.cmp(v1),
(&Value::I8(v0), Value::I8(v1)) => v0.cmp(v1),
(&Value::I16(v0), Value::I16(v1)) => v0.cmp(v1),
(&Value::I32(v0), Value::I32(v1)) => v0.cmp(v1),
(&Value::I64(v0), Value::I64(v1)) => v0.cmp(v1),
(&Value::I128(v0), Value::I128(v1)) => v0.cmp(v1),
(&Value::F32(v0), &Value::F32(v1)) => OrderedFloat(v0).cmp(&OrderedFloat(v1)),
(&Value::F64(v0), &Value::F64(v1)) => OrderedFloat(v0).cmp(&OrderedFloat(v1)),
(&Value::Char(v0), Value::Char(v1)) => v0.cmp(v1),
(Value::String(v0), Value::String(v1)) => v0.cmp(v1),
(&Value::Unit, &Value::Unit) => Ordering::Equal,
(Value::Option(v0), Value::Option(v1)) => v0.cmp(v1),
(Value::Newtype(v0), Value::Newtype(v1)) => v0.cmp(v1),
(Value::Seq(v0), Value::Seq(v1)) => v0.cmp(v1),
(Value::Map(v0), Value::Map(v1)) => v0.cmp(v1),
(Value::Bytes(v0), Value::Bytes(v1)) => v0.cmp(v1),
(&Value::CuTime(v0), &Value::CuTime(v1)) => v0.cmp(&v1),
(v0, v1) => v0.discriminant().cmp(&v1.discriminant()),
}
}
}
impl Value {
fn discriminant(&self) -> usize {
match *self {
Value::Bool(..) => 0,
Value::U8(..) => 1,
Value::U16(..) => 2,
Value::U32(..) => 3,
Value::U64(..) => 4,
Value::U128(..) => 19,
Value::I8(..) => 5,
Value::I16(..) => 6,
Value::I32(..) => 7,
Value::I64(..) => 8,
Value::I128(..) => 20,
Value::F32(..) => 9,
Value::F64(..) => 10,
Value::Char(..) => 11,
Value::String(..) => 12,
Value::Unit => 13,
Value::Option(..) => 14,
Value::Newtype(..) => 15,
Value::Seq(..) => 16,
Value::Map(..) => 17,
Value::Bytes(..) => 18,
Value::CuTime(..) => 32,
}
}
fn unexpected(&self) -> serde::de::Unexpected<'_> {
match *self {
Value::Bool(b) => serde::de::Unexpected::Bool(b),
Value::U8(n) => serde::de::Unexpected::Unsigned(n as u64),
Value::U16(n) => serde::de::Unexpected::Unsigned(n as u64),
Value::U32(n) => serde::de::Unexpected::Unsigned(n as u64),
Value::U64(n) => serde::de::Unexpected::Unsigned(n),
Value::U128(n) => match u64::try_from(n) {
Ok(n) => serde::de::Unexpected::Unsigned(n),
Err(_) => serde::de::Unexpected::Other("128-bit unsigned integer"),
},
Value::I8(n) => serde::de::Unexpected::Signed(n as i64),
Value::I16(n) => serde::de::Unexpected::Signed(n as i64),
Value::I32(n) => serde::de::Unexpected::Signed(n as i64),
Value::I64(n) => serde::de::Unexpected::Signed(n),
Value::I128(n) => match i64::try_from(n) {
Ok(n) => serde::de::Unexpected::Signed(n),
Err(_) => serde::de::Unexpected::Other("128-bit signed integer"),
},
Value::F32(n) => serde::de::Unexpected::Float(n as f64),
Value::F64(n) => serde::de::Unexpected::Float(n),
Value::Char(c) => serde::de::Unexpected::Char(c),
Value::String(ref s) => serde::de::Unexpected::Str(s),
Value::Unit => serde::de::Unexpected::Unit,
Value::Option(_) => serde::de::Unexpected::Option,
Value::Newtype(_) => serde::de::Unexpected::NewtypeStruct,
Value::Seq(_) => serde::de::Unexpected::Seq,
Value::Map(_) => serde::de::Unexpected::Map,
Value::Bytes(ref b) => serde::de::Unexpected::Bytes(b),
Value::CuTime(n) => {
let CuDuration(nanos) = n;
serde::de::Unexpected::Unsigned(nanos)
}
}
}
pub fn deserialize_into<'de, T: Deserialize<'de>>(self) -> Result<T, DeserializerError> {
T::deserialize(self)
}
}
impl Eq for Value {}
impl PartialOrd for Value {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::borrow::ToOwned;
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
#[cfg(not(feature = "std"))]
use alloc::vec;
use bincode::{borrow_decode_from_slice, config::standard, decode_from_slice, encode_to_vec};
use core::time::Duration;
use cu29_clock::{CuDuration, CuTime, RobotClock};
use serde_derive::{Deserialize, Serialize};
#[test]
fn de_smoke_test() {
let value = Value::Option(Some(Box::new(Value::Seq(vec![
Value::U16(8),
Value::Char('a'),
Value::F32(1.0),
Value::String("hello".into()),
Value::Map(
vec![
(Value::Bool(false), Value::Unit),
(
Value::Bool(true),
Value::Newtype(Box::new(Value::Bytes(b"hi".as_ref().into()))),
),
]
.into_iter()
.collect(),
),
]))));
let value_de = Value::deserialize(value.clone()).unwrap();
assert_eq!(value_de, value);
}
#[test]
fn ser_smoke_test() {
#[derive(Serialize, Deserialize)]
struct Foo {
a: u32,
b: String,
c: Vec<bool>,
}
let foo = Foo {
a: 15,
b: "hello".into(),
c: vec![true, false],
};
let expected = Value::Map(
vec![
(Value::String("a".into()), Value::U32(15)),
(Value::String("b".into()), Value::String("hello".into())),
(
Value::String("c".into()),
Value::Seq(vec![Value::Bool(true), Value::Bool(false)]),
),
]
.into_iter()
.collect(),
);
let value = to_value(&foo).unwrap();
assert_eq!(expected, value);
}
#[test]
fn deserialize_into_enum() {
#[derive(Deserialize, Debug, PartialEq, Eq)]
enum Foo {
Bar,
Baz(u8),
}
let value = Value::String("Bar".into());
assert_eq!(Foo::deserialize(value).unwrap(), Foo::Bar);
let value = Value::Map(
vec![(Value::String("Baz".into()), Value::U8(1))]
.into_iter()
.collect(),
);
assert_eq!(Foo::deserialize(value).unwrap(), Foo::Baz(1));
}
#[test]
fn serialize_from_enum() {
#[derive(Serialize, Deserialize)]
enum Foo {
Bar,
Baz(u8),
Qux { quux: u8 },
Corge(u8, u8),
}
let bar = Foo::Bar;
assert_eq!(to_value(&bar).unwrap(), Value::String("Bar".into()));
let baz = Foo::Baz(1);
assert_eq!(
to_value(&baz).unwrap(),
Value::Map(
vec![(Value::String("Baz".into()), Value::U8(1))]
.into_iter()
.collect(),
)
);
let qux = Foo::Qux { quux: 2 };
assert_eq!(
to_value(&qux).unwrap(),
Value::Map(
vec![(
Value::String("Qux".into()),
Value::Map(
vec![(Value::String("quux".into()), Value::U8(2))]
.into_iter()
.collect()
)
)]
.into_iter()
.collect()
)
);
let corge = Foo::Corge(3, 4);
assert_eq!(
to_value(&corge).unwrap(),
Value::Map(
vec![(
Value::String("Corge".into()),
Value::Seq(vec![Value::U8(3), Value::U8(4)])
)]
.into_iter()
.collect()
)
);
}
#[test]
fn deserialize_inside_deserialize_impl() {
#[derive(Debug, PartialEq, Eq)]
enum Event {
Added(u32),
Error(u8),
}
impl<'de> serde::Deserialize<'de> for Event {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
struct RawEvent {
kind: String,
object: Value,
}
let raw_event = RawEvent::deserialize(deserializer)?;
let object_deserializer = ValueDeserializer::new(raw_event.object);
Ok(match &*raw_event.kind {
"ADDED" => Event::Added(<_>::deserialize(object_deserializer)?),
"ERROR" => Event::Error(<_>::deserialize(object_deserializer)?),
kind => {
return Err(serde::de::Error::unknown_variant(kind, &["ADDED", "ERROR"]));
}
})
}
}
let input = Value::Map(
vec![
(
Value::String("kind".to_owned()),
Value::String("ADDED".to_owned()),
),
(Value::String("object".to_owned()), Value::U32(5)),
]
.into_iter()
.collect(),
);
let event = Event::deserialize(input).expect("could not deserialize ADDED event");
assert_eq!(event, Event::Added(5));
let input = Value::Map(
vec![
(
Value::String("kind".to_owned()),
Value::String("ERROR".to_owned()),
),
(Value::String("object".to_owned()), Value::U8(5)),
]
.into_iter()
.collect(),
);
let event = Event::deserialize(input).expect("could not deserialize ERROR event");
assert_eq!(event, Event::Error(5));
let input = Value::Map(
vec![
(
Value::String("kind".to_owned()),
Value::String("ADDED".to_owned()),
),
(Value::String("object".to_owned()), Value::Unit),
]
.into_iter()
.collect(),
);
let _ =
Event::deserialize(input).expect_err("expected deserializing bad ADDED event to fail");
}
#[test]
fn deserialize_newtype() {
#[derive(Debug, Deserialize, PartialEq)]
struct Foo(i32);
let input = Value::I32(5);
let foo = Foo::deserialize(input).unwrap();
assert_eq!(foo, Foo(5));
}
#[test]
fn deserialize_newtype2() {
#[derive(Debug, Deserialize, PartialEq)]
struct Foo(i32);
#[derive(Debug, Deserialize, PartialEq)]
struct Bar {
foo: Foo,
}
let input = Value::Map(
vec![(Value::String("foo".to_owned()), Value::I32(5))]
.into_iter()
.collect(),
);
let bar = Bar::deserialize(input).unwrap();
assert_eq!(bar, Bar { foo: Foo(5) });
}
#[test]
fn clock_ser_deser() {
let (clock, mock) = RobotClock::mock();
mock.increment(CuDuration::from_nanos(42));
let c = clock.now();
let input = Value::CuTime(c);
let foo = CuTime::deserialize(input).unwrap();
assert_eq!(foo, CuTime::from(Duration::from_nanos(42)));
}
#[test]
fn value_encode_decode() {
fn check_value(value: Value) {
let v = bincode::encode_to_vec(&value, standard()).expect("encode failed");
let (v2, s) = bincode::decode_from_slice::<Value, _>(v.as_slice(), standard())
.expect("decode failed");
assert_eq!(s, v.len());
assert_eq!(&v2, &value);
}
check_value(Value::Bool(true));
check_value(Value::U8(42));
check_value(Value::U16(42));
check_value(Value::U32(42));
check_value(Value::U64(42));
check_value(Value::U128(u128::from(u64::MAX) + 42));
check_value(Value::I8(42));
check_value(Value::I16(42));
check_value(Value::I32(42));
check_value(Value::I64(42));
check_value(Value::I128(i128::from(i64::MIN) - 42));
check_value(Value::F32(42.42));
check_value(Value::F64(42.42));
check_value(Value::Char('4'));
check_value(Value::String("42".into()));
check_value(Value::Unit);
check_value(Value::Option(Some(Box::new(Value::U32(42)))));
check_value(Value::Newtype(Box::new(Value::U32(42))));
check_value(Value::Seq(vec![Value::Bool(true), Value::U32(42)]));
check_value(Value::Map(BTreeMap::from([
(Value::Bool(true), Value::U32(42)),
(Value::String("42".into()), Value::I32(42)),
])));
check_value(Value::Bytes(vec![0x4, 0x2]));
check_value(Value::CuTime(CuTime::from(Duration::from_nanos(42))));
}
#[test]
fn value_borrow_decode() {
fn check_value(value: Value) {
let encoded = encode_to_vec(&value, standard()).expect("encode failed");
let (decoded, size) =
borrow_decode_from_slice::<Value, _>(encoded.as_slice(), standard())
.expect("borrow decode failed");
assert_eq!(size, encoded.len());
assert_eq!(decoded, value);
}
check_value(Value::Bool(true));
check_value(Value::U128(u128::from(u64::MAX) + 7));
check_value(Value::I128(i128::from(i64::MIN) - 7));
check_value(Value::String("borrow".into()));
check_value(Value::Bytes(vec![1, 2, 3]));
check_value(Value::CuTime(CuTime::from(Duration::from_nanos(42))));
}
#[test]
fn test_cutime_tovalue() {
let c = CuTime::from(Duration::from_nanos(42));
let v = to_value(c).expect("to_value failed");
assert_eq!(v, Value::CuTime(c));
}
#[test]
fn test_value_creation_and_types() {
let bool_val = Value::Bool(true);
let i32_val = Value::I32(42);
let str_val = Value::String("test".to_string());
let unit_val = Value::Unit;
let option_val = Value::Option(Some(Box::new(Value::U8(5))));
assert!(matches!(bool_val, Value::Bool(true)));
assert!(matches!(i32_val, Value::I32(42)));
assert!(matches!(str_val, Value::String(ref s) if s == "test"));
assert!(matches!(unit_val, Value::Unit));
assert!(matches!(option_val, Value::Option(Some(_))));
assert_eq!(bool_val.discriminant(), 0);
assert_eq!(i32_val.discriminant(), 7);
assert_eq!(str_val.discriminant(), 12);
}
#[test]
fn test_numeric_boundaries_and_special_values() {
let min_i8 = Value::I8(i8::MIN);
let max_i8 = Value::I8(i8::MAX);
let min_i64 = Value::I64(i64::MIN);
let max_u64 = Value::U64(u64::MAX);
let min_i128 = Value::I128(i128::MIN);
let max_u128 = Value::U128(u128::MAX);
let nan = Value::F64(f64::NAN);
let pos_inf = Value::F64(f64::INFINITY);
let neg_inf = Value::F64(f64::NEG_INFINITY);
let zero = Value::F64(0.0);
let neg_zero = Value::F64(-0.0);
for val in [
min_i8,
max_i8,
min_i64,
max_u64,
min_i128,
max_u128,
nan.clone(),
pos_inf.clone(),
neg_inf.clone(),
zero.clone(),
neg_zero.clone(),
] {
let encoded = encode_to_vec(&val, standard()).unwrap();
let (decoded, _): (Value, _) = decode_from_slice(&encoded, standard()).unwrap();
if matches!(val, Value::F64(f) if f.is_nan()) {
assert!(matches!(decoded, Value::F64(f) if f.is_nan()));
} else {
assert_eq!(val, decoded);
}
}
assert!(pos_inf > zero);
assert!(neg_inf < zero);
let nan2 = Value::F64(f64::NAN);
assert_eq!(nan, nan2);
assert_eq!(zero, neg_zero);
}
#[test]
fn test_container_types() {
let empty_seq = Value::Seq(vec![]);
let empty_map = Value::Map(BTreeMap::new());
let simple_seq = Value::Seq(vec![Value::I32(1), Value::I32(2), Value::I32(3)]);
let mut simple_map = BTreeMap::new();
simple_map.insert(Value::String("key".to_string()), Value::Bool(true));
let simple_map_val = Value::Map(simple_map);
let mut nested_map = BTreeMap::new();
nested_map.insert(
Value::String("outer".to_string()),
Value::Seq(vec![
Value::Option(Some(Box::new(Value::Map({
let mut m = BTreeMap::new();
m.insert(Value::I32(1), Value::String("nested".to_string()));
m
})))),
Value::Bool(false),
]),
);
let nested_val = Value::Map(nested_map);
for val in [empty_seq, empty_map, simple_seq, simple_map_val, nested_val] {
let encoded = encode_to_vec(&val, standard()).unwrap();
let (decoded, _): (Value, _) = decode_from_slice(&encoded, standard()).unwrap();
assert_eq!(val, decoded);
}
}
#[test]
fn test_large_values() {
let large_seq = Value::Seq((0..10000).map(Value::I32).collect());
let large_string = Value::String("x".repeat(100000));
let large_bytes = Value::Bytes((0..10000).map(|i| (i % 256) as u8).collect());
let mut large_map = BTreeMap::new();
for i in 0..1000 {
large_map.insert(
Value::I32(i),
Value::Seq((0..10).map(|j| Value::I32(i * j)).collect()),
);
}
let large_nested = Value::Map(large_map);
for val in [large_seq, large_string, large_bytes, large_nested] {
let encoded = encode_to_vec(&val, standard()).unwrap();
let (decoded, _): (Value, _) = decode_from_slice(&encoded, standard()).unwrap();
assert_eq!(val, decoded);
}
}
#[test]
fn test_value_comparison() {
assert!(Value::I32(1) < Value::I32(2));
assert!(Value::String("a".to_string()) < Value::String("b".to_string()));
assert!(Value::Bool(false) < Value::Bool(true));
assert!(Value::Bool(true) < Value::I32(0)); assert!(Value::I32(100) < Value::String("a".to_string()));
assert!(
Value::Seq(vec![Value::I32(1), Value::I32(2)])
< Value::Seq(vec![Value::I32(1), Value::I32(3)])
);
let mut map1 = BTreeMap::new();
map1.insert(Value::String("key".to_string()), Value::I32(1));
let mut map2 = BTreeMap::new();
map2.insert(Value::String("key".to_string()), Value::I32(2));
assert!(Value::Map(map1) < Value::Map(map2));
let nan1 = Value::F64(f64::NAN);
let nan2 = Value::F64(f64::NAN);
assert_eq!(nan1, nan2); }
#[test]
#[cfg(feature = "std")]
fn test_value_hashing() {
use core::hash::Hasher;
use std::collections::hash_map::DefaultHasher;
let values = [
Value::Bool(true),
Value::I32(42),
Value::String("hash me".to_string()),
Value::F64(3.1),
Value::Char('🦀'),
Value::Option(Some(Box::new(Value::U8(5)))),
Value::Unit,
];
for val in values {
let mut hasher1 = DefaultHasher::new();
let mut hasher2 = DefaultHasher::new();
val.hash(&mut hasher1);
val.hash(&mut hasher2);
assert_eq!(hasher1.finish(), hasher2.finish());
let val_clone = val.clone();
let mut hasher3 = DefaultHasher::new();
val_clone.hash(&mut hasher3);
assert_eq!(hasher1.finish(), hasher3.finish());
}
let nan1 = Value::F64(f64::NAN);
let nan2 = Value::F64(f64::NAN);
let mut hasher1 = DefaultHasher::new();
let mut hasher2 = DefaultHasher::new();
nan1.hash(&mut hasher1);
nan2.hash(&mut hasher2);
assert_eq!(hasher1.finish(), hasher2.finish());
}
#[test]
fn test_struct_serde() {
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Person {
name: String,
age: u32,
addresses: Vec<Address>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Address {
street: String,
city: String,
zip: u32,
}
let person = Person {
name: "Alice".to_string(),
age: 30,
addresses: vec![
Address {
street: "123 Main St".to_string(),
city: "Anytown".to_string(),
zip: 12345,
},
Address {
street: "456 Oak Ave".to_string(),
city: "Somewhere".to_string(),
zip: 67890,
},
],
};
let value = to_value(&person).unwrap();
assert!(matches!(value, Value::Map(_)));
let person2 = value.deserialize_into::<Person>().unwrap();
assert_eq!(person, person2);
}
#[test]
fn test_enum_serde() {
#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum MyEnum {
Unit,
NewType(i32),
Tuple(String, bool),
Struct { x: f64, y: f64 },
}
let variants = vec![
MyEnum::Unit,
MyEnum::NewType(42),
MyEnum::Tuple("hello".to_string(), true),
MyEnum::Struct { x: 1.0, y: 2.0 },
];
for variant in variants {
let value = to_value(&variant).unwrap();
let roundtrip = value.deserialize_into::<MyEnum>().unwrap();
assert_eq!(variant, roundtrip);
}
}
#[test]
fn test_cutime_handling() {
let times = vec![
CuTime::from(CuDuration(0)),
CuTime::from(CuDuration(1)),
CuTime::from(CuDuration(u64::MAX / 2)),
];
for time in times {
let time_value = Value::CuTime(time);
let encoded = encode_to_vec(&time_value, standard()).unwrap();
let (decoded, _): (Value, _) = decode_from_slice(&encoded, standard()).unwrap();
assert_eq!(time_value, decoded);
let via_to_value = to_value(time).unwrap();
assert_eq!(via_to_value, time_value);
let time_roundtrip = via_to_value.deserialize_into::<CuTime>().unwrap();
assert_eq!(time, time_roundtrip);
}
}
#[test]
fn test_error_handling() {
let bool_val = Value::Bool(true);
let result = bool_val.clone().deserialize_into::<i32>();
assert!(result.is_err());
let empty_map = Value::Map(BTreeMap::new());
#[derive(Deserialize)]
struct RequiredFields {
_required: String,
}
let result = empty_map.deserialize_into::<RequiredFields>();
assert!(result.is_err());
let invalid_variant = Value::String("NonExistentVariant".to_string());
#[derive(Deserialize)]
enum TestEnum {
A,
B,
C,
}
let result = invalid_variant.deserialize_into::<TestEnum>();
assert!(result.is_err());
match bool_val.deserialize_into::<String>() {
Err(DeserializerError::InvalidType(..)) => (), other => panic!("Expected InvalidType error, got: {other:?}"),
}
}
#[test]
fn test_unicode_handling() {
let strings = vec![
"".to_string(), "ASCII only".to_string(), "Café 🍰".to_string(), "日本語".to_string(), "👨👩👧👦 Family".to_string(), "ᛁᚲ ᚲᚨᚾ ᚱᚢᚾᛖᛋ".to_string(), ];
for s in strings {
let string_val = Value::String(s.clone());
let encoded = encode_to_vec(&string_val, standard()).unwrap();
let (decoded, _): (Value, _) = decode_from_slice(&encoded, standard()).unwrap();
if let Value::String(decoded_s) = decoded {
assert_eq!(s, decoded_s);
} else {
panic!("Expected String value");
}
}
let chars = vec!['a', 'é', '日', '🦀'];
for c in chars {
let char_val = Value::Char(c);
let encoded = encode_to_vec(&char_val, standard()).unwrap();
let (decoded, _): (Value, _) = decode_from_slice(&encoded, standard()).unwrap();
if let Value::Char(decoded_c) = decoded {
assert_eq!(c, decoded_c);
} else {
panic!("Expected Char value");
}
}
}
#[test]
fn test_value_deserializer() {
let original = vec![1, 2, 3];
let value = to_value(&original).unwrap();
let deserializer: de::ValueDeserializer<DeserializerError> = ValueDeserializer::new(value);
let result: Vec<i32> = serde::Deserialize::deserialize(deserializer).unwrap();
assert_eq!(original, result);
}
#[test]
fn test_option_handling() {
let some_i32 = Some(42);
let some_string = Some("test".to_string());
let none_i32: Option<i32> = None;
let none_string: Option<String> = None;
let some_i32_value = to_value(some_i32).unwrap();
let some_string_value = to_value(&some_string).unwrap();
let none_i32_value = to_value(none_i32).unwrap();
let none_string_value = to_value(&none_string).unwrap();
assert!(matches!(some_i32_value, Value::Option(Some(_))));
assert!(matches!(some_string_value, Value::Option(Some(_))));
assert!(matches!(none_i32_value, Value::Option(None)));
assert!(matches!(none_string_value, Value::Option(None)));
let some_i32_rt: Option<i32> = some_i32_value.deserialize_into().unwrap();
let some_string_rt: Option<String> = some_string_value.deserialize_into().unwrap();
let none_i32_rt: Option<i32> = none_i32_value.deserialize_into().unwrap();
let none_string_rt: Option<String> = none_string_value.deserialize_into().unwrap();
assert_eq!(some_i32, some_i32_rt);
assert_eq!(some_string, some_string_rt);
assert_eq!(none_i32, none_i32_rt);
assert_eq!(none_string, none_string_rt);
}
#[test]
fn test_nested_options() {
let nested_option: Option<Option<Option<i32>>> = Some(Some(Some(42)));
let value = to_value(nested_option).unwrap();
let mut current = &value;
for _ in 0..3 {
assert!(matches!(current, Value::Option(Some(_))));
if let Value::Option(Some(inner)) = current {
current = inner;
} else {
panic!("Expected Some");
}
}
assert!(matches!(current, Value::I32(42)));
let result: Option<Option<Option<i32>>> = value.deserialize_into().unwrap();
assert_eq!(nested_option, result);
}
#[test]
fn test_numeric_conversions() {
let i8_val = Value::I8(42);
let i16_val = Value::I16(42);
let i32_val = Value::I32(42);
let i64_val = Value::I64(42);
let i128_val = Value::I128(42);
let u8_val = Value::U8(42);
let u16_val = Value::U16(42);
let u32_val = Value::U32(42);
let u64_val = Value::U64(42);
let u64_val_large = Value::U64(u64::MAX);
let u128_val = Value::U128(42);
let u128_val_large = Value::U128(u128::from(u64::MAX) + 1);
let f32_val = Value::F32(42.0);
let f64_val = Value::F64(42.0);
assert!(i8_val.deserialize_into::<i16>().is_ok());
assert!(i16_val.deserialize_into::<i32>().is_ok());
assert!(i32_val.clone().deserialize_into::<i64>().is_ok());
assert!(u8_val.deserialize_into::<u16>().is_ok());
assert!(u16_val.deserialize_into::<u32>().is_ok());
assert!(u32_val.deserialize_into::<u64>().is_ok());
assert!(u64_val.clone().deserialize_into::<u128>().is_ok());
assert!(u128_val.deserialize_into::<u128>().is_ok());
assert!(u64_val.clone().deserialize_into::<f64>().is_ok());
assert!(i32_val.deserialize_into::<f32>().is_ok());
assert!(f32_val.deserialize_into::<f64>().is_ok());
assert!(i64_val.clone().deserialize_into::<f64>().is_ok());
assert!(i64_val.clone().deserialize_into::<i128>().is_ok());
assert!(i128_val.deserialize_into::<i128>().is_ok());
assert!(u64_val.deserialize_into::<i8>().is_ok());
assert!(u64_val_large.deserialize_into::<i8>().is_err());
assert!(u128_val_large.deserialize_into::<u64>().is_err());
assert!(f64_val.deserialize_into::<u32>().is_err());
assert!(i64_val.deserialize_into::<bool>().is_err());
}
#[test]
fn test_display_implementation() {
let values = [
(Value::Bool(true), "true"),
(Value::I32(42), "42"),
(Value::String("test".to_string()), "test"),
(Value::Unit, "()"),
(
Value::CuTime(CuTime::from(CuDuration(1_000_000_000))),
"1.000 s",
),
];
for (val, expected) in values {
assert_eq!(val.to_string(), expected);
}
let seq = Value::Seq(vec![Value::I32(1), Value::I32(2), Value::I32(3)]);
assert_eq!(seq.to_string(), "[1, 2, 3]");
let mut map = BTreeMap::new();
map.insert(Value::String("key".to_string()), Value::Bool(true));
let map_val = Value::Map(map);
assert_eq!(map_val.to_string(), "{key: true}");
}
#[test]
fn test_numeric_overflow_detection() {
let large_i64 = Value::I64(i64::MAX);
assert!(large_i64.deserialize_into::<i32>().is_err());
let large_u128 = Value::U128(u128::from(u64::MAX) + 1);
assert!(large_u128.deserialize_into::<u64>().is_err());
let negative = Value::I64(-1);
assert!(negative.deserialize_into::<u64>().is_err());
let negative_i128 = Value::I128(-1);
assert!(negative_i128.deserialize_into::<u64>().is_err());
let max_i32 = Value::I64(i32::MAX as i64);
assert!(max_i32.deserialize_into::<i32>().is_ok());
let beyond_max_i32 = Value::I64((i32::MAX as i64) + 1);
assert!(beyond_max_i32.deserialize_into::<i32>().is_err());
}
#[test]
fn test_u128_and_i128_round_trip() {
let u128_value = to_value(u128::from(u64::MAX) + 99).expect("to_value failed");
assert_eq!(u128_value, Value::U128(u128::from(u64::MAX) + 99));
let round_trip_u128: u128 = u128_value
.deserialize_into()
.expect("u128 round-trip failed");
assert_eq!(round_trip_u128, u128::from(u64::MAX) + 99);
let i128_value = to_value(i128::from(i64::MIN) - 99).expect("to_value failed");
assert_eq!(i128_value, Value::I128(i128::from(i64::MIN) - 99));
let round_trip_i128: i128 = i128_value
.deserialize_into()
.expect("i128 round-trip failed");
assert_eq!(round_trip_i128, i128::from(i64::MIN) - 99);
}
#[test]
fn test_float_precision_handling() {
let original = i64::MAX;
let as_value = Value::I64(original);
let as_f64: f64 = as_value.deserialize_into().unwrap();
let round_trip = Value::F64(as_f64).deserialize_into::<i64>();
assert!(round_trip.is_err());
let half = Value::F64(0.5);
let half_as_i32 = half.deserialize_into::<i32>();
assert!(half_as_i32.is_err());
}
#[test]
fn test_float_special_values() {
let nan = Value::F64(f64::NAN);
assert!(nan.deserialize_into::<i32>().is_err());
let infinity = Value::F64(f64::INFINITY);
assert!(infinity.deserialize_into::<i64>().is_err());
let huge = Value::F64(1e20);
assert!(huge.deserialize_into::<i64>().is_err());
}
}