use anyhow::Result;
use musli::alloc::Allocator;
use musli::alloc::Global;
use musli::de::{AsDecoder, Decoder};
use musli::json;
use musli::mode::Text;
use musli::value::{self, Value};
use musli::{Decode, Encode};
#[derive(Decode, Encode)]
struct Struct {
#[musli(default, skip_encoding_if = Option::is_none)]
field: Option<u32>,
}
#[test]
fn option() -> Result<()> {
let value: Value<Global> = json::from_str(r#"{"field":null}"#)?;
let st: Struct = value::decode_text(&value)?;
assert_eq!(st.field, None);
let value: Value<Global> = json::from_str(r#"{"field":42}"#)?;
let st: Struct = value::decode_text(&value)?;
assert_eq!(st.field, Some(42));
let value: Value<Global> = json::from_str(r#"{}"#)?;
let st: Struct = value::decode_text(&value)?;
assert_eq!(st.field, None);
Ok(())
}
#[test]
fn number_coercion() -> Result<()> {
let value = Value::<Global>::from(42u8);
assert_eq!(value::decode::<u64>(&value)?, 42u64);
assert_eq!(value::decode::<i32>(&value)?, 42i32);
assert_eq!(value::decode::<f32>(&value)?, 42.0f32);
let value = Value::<Global>::from(-1i64);
assert_eq!(value::decode::<i8>(&value)?, -1i8);
assert_eq!(value::decode::<f64>(&value)?, -1.0f64);
assert!(value::decode::<u32>(&value).is_err());
let value = Value::<Global>::from(3.5f32);
assert_eq!(value::decode::<f64>(&value)?, 3.5f64);
assert_eq!(value::decode::<u32>(&value)?, 3u32);
let value = Value::<Global>::from(u128::MAX);
assert_eq!(value::decode::<u128>(&value)?, u128::MAX);
assert!(value::decode::<i128>(&value).is_err());
let value = Value::<Global>::from(i128::MIN);
assert_eq!(value::decode::<i128>(&value)?, i128::MIN);
Ok(())
}
#[test]
fn float_to_integer_coercion() -> Result<()> {
assert_eq!(value::decode::<i32>(&Value::<Global>::from(-3.5f64))?, -3);
assert_eq!(value::decode::<u8>(&Value::<Global>::from(255.9f64))?, 255);
assert_eq!(
value::decode::<u64>(&Value::<Global>::from(9.007199254740992e15f64))?,
9007199254740992
);
assert!(value::decode::<u8>(&Value::<Global>::from(1e30f64)).is_err());
assert!(value::decode::<u64>(&Value::<Global>::from(1e30f64)).is_err());
assert!(value::decode::<u32>(&Value::<Global>::from(-1.0f64)).is_err());
assert!(value::decode::<i8>(&Value::<Global>::from(128.0f64)).is_err());
assert!(value::decode::<u64>(&Value::<Global>::from(1.8446744073709552e19f64)).is_err());
assert!(value::decode::<u8>(&Value::<Global>::from(f64::NAN)).is_err());
assert!(value::decode::<i32>(&Value::<Global>::from(f64::INFINITY)).is_err());
assert!(value::decode::<i32>(&Value::<Global>::from(f64::NEG_INFINITY)).is_err());
Ok(())
}
#[test]
fn numeric_map_keys() -> Result<()> {
use std::collections::BTreeMap;
for (input, expected) in [
(r#"{}"#, BTreeMap::new()),
(r#"{"1":2}"#, BTreeMap::from([(1u32, 2u32)])),
(
r#"{"0":1,"4294967295":2}"#,
BTreeMap::from([(0u32, 1u32), (u32::MAX, 2u32)]),
),
] {
assert_eq!(json::from_str::<BTreeMap<u32, u32>>(input)?, expected);
let value: Value<Global> = json::from_str(input)?;
assert_eq!(value::decode_text::<BTreeMap<u32, u32>>(&value)?, expected);
}
let expected = BTreeMap::from([(i64::MIN, 1u32), (-1i64, 2u32)]);
let value: Value<Global> = json::from_str(&json::to_string(&expected)?)?;
assert_eq!(value::decode_text::<BTreeMap<i64, u32>>(&value)?, expected);
let expected = BTreeMap::from([(u128::MAX, 1u32)]);
let value: Value<Global> = json::from_str(&json::to_string(&expected)?)?;
assert_eq!(value::decode_text::<BTreeMap<u128, u32>>(&value)?, expected);
let value: Value<Global> = json::from_str(r#"{"1":2}"#)?;
assert!(value::decode::<BTreeMap<u32, u32>>(&value).is_err());
Ok(())
}
#[test]
fn number_representation() -> Result<()> {
let value: Value<Global> = value::encode(-3i16)?;
assert!(value.is_i16());
assert_eq!(format!("{value:?}"), "-3");
let value: Value<Global> = value::encode(1.5f32)?;
assert!(value.is_f32());
assert_eq!(format!("{value:?}"), "1.5");
Ok(())
}
#[test]
fn platform_sized_integers() -> Result<()> {
let value: Value<Global> = value::encode(42usize)?;
assert!(value.is_usize());
#[cfg(target_pointer_width = "32")]
assert_eq!(value, Value::from(42u32));
#[cfg(target_pointer_width = "64")]
assert_eq!(value, Value::from(42u64));
let value: Value<Global> = value::encode(-42isize)?;
assert!(value.is_isize());
#[cfg(target_pointer_width = "32")]
assert_eq!(value, Value::from(-42i32));
#[cfg(target_pointer_width = "64")]
assert_eq!(value, Value::from(-42i64));
Ok(())
}
#[derive(Debug, PartialEq, Decode, Encode)]
enum Enum {
Unit,
Tuple(u32, String),
Struct { a: u32, b: String },
}
#[test]
fn json_enum() -> Result<()> {
for expected in [
Enum::Unit,
Enum::Tuple(42, String::from("hello")),
Enum::Struct {
a: 42,
b: String::from("hello"),
},
] {
let json = json::to_string(&expected)?;
let value: Value<Global> = json::from_str(&json)?;
let actual: Enum = value::decode_text(&value)?;
assert_eq!(actual, expected, "{json}");
}
Ok(())
}
#[test]
fn json_enum_malformed() -> Result<()> {
for json in [
r#"{}"#,
r#"{"Unit":{},"Unit":{}}"#,
r#"{"Unit":{},"Struct":{"a":42,"b":"hello"}}"#,
r#"[]"#,
r#"42"#,
] {
let value: Value<Global> = json::from_str(json)?;
assert!(value::decode_text::<Enum>(&value).is_err(), "{json}");
}
let value: Value<Global> = json::from_str(r#"{"Missing":{}}"#)?;
assert!(value::decode_text::<Enum>(&value).is_err());
let value: Value<Global> = json::from_str(r#"{"Struct":{"a":"wrong"}}"#)?;
assert!(value::decode_text::<Enum>(&value).is_err());
Ok(())
}
#[derive(Debug, PartialEq)]
struct Buffered(Enum);
impl<'de, A> Decode<'de, Text, A> for Buffered
where
A: Allocator,
{
const IS_BITWISE_DECODE: bool = false;
#[inline]
fn decode<D>(decoder: D) -> Result<Self, D::Error>
where
D: Decoder<'de, Mode = Text, Allocator = A>,
{
let buffer = decoder.decode_buffer()?;
Ok(Buffered(buffer.as_decoder()?.decode()?))
}
}
#[test]
fn json_enum_buffered() -> Result<()> {
let expected = Enum::Struct {
a: 42,
b: String::from("hello"),
};
let json = json::to_string(&expected)?;
let Buffered(actual) = json::from_str(&json)?;
assert_eq!(actual, expected);
Ok(())
}