use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Unit;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Wrapper(u64);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Pair(i8, u16);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Inner {
flag: bool,
name: String,
ch: char,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
enum Shape {
Unit,
Newtype(i32),
Tuple(i32, String),
Struct { x: f64, y: f64 },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Everything {
i8_: i8,
i16_: i16,
i32_: i32,
i64_: i64,
u8_: u8,
u16_: u16,
u32_: u32,
u64_: u64,
f32_: f32,
f64_: f64,
b: bool,
s: String,
c: char,
opt_some: Option<i32>,
opt_none: Option<i32>,
unit: Unit,
newtype: Wrapper,
tuple_struct: Pair,
tuple: (i32, String, bool),
seq: Vec<i32>,
nested: Inner,
variants: Vec<Shape>,
map: BTreeMap<String, i32>,
}
fn sample() -> Everything {
Everything {
i8_: -8,
i16_: -1600,
i32_: -320_000,
i64_: -6_400_000_000,
u8_: 8,
u16_: 1600,
u32_: 320_000,
u64_: 6_400_000_000,
f32_: 1.5,
f64_: -2.25,
b: true,
s: "hello".to_owned(),
c: 'λ',
opt_some: Some(7),
opt_none: None,
unit: Unit,
newtype: Wrapper(42),
tuple_struct: Pair(-3, 9),
tuple: (1, "two".to_owned(), false),
seq: vec![1, 2, 3, 4],
nested: Inner {
flag: false,
name: "inner".to_owned(),
ch: 'x',
},
variants: vec![
Shape::Unit,
Shape::Newtype(-5),
Shape::Tuple(6, "t".to_owned()),
Shape::Struct { x: 0.5, y: -0.25 },
],
map: BTreeMap::from([("a".to_owned(), 1), ("b".to_owned(), 2)]),
}
}
#[test]
fn round_trips_a_derived_value() {
let original = sample();
let value = ijson::to_value(&original).expect("serialize into IValue");
let restored: Everything = ijson::from_value(&value).expect("deserialize from IValue");
assert_eq!(original, restored);
}
#[test]
fn round_trips_each_enum_variant() {
for variant in [
Shape::Unit,
Shape::Newtype(-5),
Shape::Tuple(6, "t".to_owned()),
Shape::Struct { x: 0.5, y: -0.25 },
] {
let value = ijson::to_value(&variant).expect("serialize variant");
let restored: Shape = ijson::from_value(&value).expect("deserialize variant");
assert_eq!(variant, restored);
}
}
#[test]
fn enum_encoding_shape() {
let unit = ijson::to_value(Shape::Unit).unwrap();
assert_eq!(unit.as_string().map(|s| s.as_str()), Some("Unit"));
let newtype = ijson::to_value(Shape::Newtype(9)).unwrap();
let obj = newtype.as_object().expect("data variant is an object");
assert_eq!(obj.len(), 1);
assert_eq!(obj["Newtype"].to_i64(), Some(9));
}
#[test]
fn non_string_map_keys_serialize_as_strings() {
let int_keyed: BTreeMap<u32, i32> = BTreeMap::from([(1, 10), (2, 20)]);
let value = ijson::to_value(&int_keyed).unwrap();
let obj = value.as_object().expect("map serializes as object");
assert_eq!(obj["1"].to_i64(), Some(10));
assert_eq!(obj["2"].to_i64(), Some(20));
let char_keyed: BTreeMap<char, i32> = BTreeMap::from([('a', 1)]);
let value = ijson::to_value(&char_keyed).unwrap();
assert_eq!(value.as_object().unwrap()["a"].to_i64(), Some(1));
}
#[test]
fn unrepresentable_map_key_is_rejected() {
let bad: BTreeMap<bool, i32> = BTreeMap::from([(true, 1)]);
assert!(ijson::to_value(&bad).is_err());
}
#[test]
fn extra_object_fields_are_ignored() {
let mut obj = ijson::to_value(&Inner {
flag: true,
name: "n".to_owned(),
ch: 'z',
})
.unwrap();
obj.as_object_mut()
.unwrap()
.insert("unexpected", ijson::IValue::from(123));
let restored: Inner = ijson::from_value(&obj).expect("extra field ignored");
assert_eq!(restored.name, "n");
}
#[test]
fn type_mismatches_are_errors() {
let s = ijson::IValue::from("text");
let n = ijson::IValue::from(5);
let arr: ijson::IValue = ijson::IArray::new().into();
assert!(ijson::from_value::<u32>(&s).is_err()); assert!(ijson::from_value::<String>(&n).is_err()); assert!(ijson::from_value::<bool>(&n).is_err()); assert!(ijson::from_value::<Vec<i32>>(&n).is_err()); assert!(ijson::from_value::<Inner>(&arr).is_err()); assert!(ijson::from_value::<Inner>(&n).is_err()); assert!(ijson::from_value::<Shape>(&n).is_err()); }
#[derive(Serialize, PartialEq, Eq, PartialOrd, Ord)]
enum UnitKey {
A,
B,
}
#[derive(Serialize, PartialEq, Eq, PartialOrd, Ord)]
struct IdKey(u32);
#[test]
fn serializable_map_key_shapes() {
let signed = ijson::to_value(BTreeMap::from([(-8i8, 1), (8, 2)])).unwrap();
assert_eq!(signed.as_object().unwrap()["-8"].to_i64(), Some(1));
let wide = ijson::to_value(BTreeMap::from([(1i64 << 40, 1)])).unwrap();
assert_eq!(wide.as_object().unwrap()["1099511627776"].to_i64(), Some(1));
let unsigned = ijson::to_value(BTreeMap::from([(200u8, 1)])).unwrap();
assert_eq!(unsigned.as_object().unwrap()["200"].to_i64(), Some(1));
let by_variant = ijson::to_value(BTreeMap::from([(UnitKey::A, 1), (UnitKey::B, 2)])).unwrap();
let o = by_variant.as_object().unwrap();
assert_eq!(o["A"].to_i64(), Some(1));
assert_eq!(o["B"].to_i64(), Some(2));
let by_id = ijson::to_value(BTreeMap::from([(IdKey(7), 1)])).unwrap();
assert_eq!(by_id.as_object().unwrap()["7"].to_i64(), Some(1));
}
#[test]
fn non_scalar_map_keys_are_rejected() {
assert!(ijson::to_value(BTreeMap::from([((1, 2), 0)])).is_err()); assert!(ijson::to_value(BTreeMap::from([(vec![1, 2], 0)])).is_err()); assert!(ijson::to_value(BTreeMap::from([((), 0)])).is_err()); assert!(ijson::to_value(BTreeMap::from([(Some(1), 0)])).is_err()); assert!(ijson::to_value(BTreeMap::from([(None::<i32>, 0)])).is_err()); }