#![cfg(all(
feature = "std",
feature = "alloc",
feature = "json",
feature = "value"
))]
use std::fmt::Write;
use musli::alloc::Global;
use musli::value::Value;
const LITERALS: &[&str] = &[
"0.6",
"0.35",
"0.3",
"0.7",
"8.98846567431158e307",
"2.2250738585072014e-308",
"1e-320",
"5e-324",
"1.7976931348623157e308",
];
#[track_caller]
fn via_value<T>(s: &str) -> T
where
T: for<'de> musli::Decode<'de, musli::mode::Binary, Global>,
{
let value: Value<Global> = musli::json::from_str(s).expect("Failed to decode JSON");
musli::value::decode(&value).expect("Failed to decode value")
}
#[test]
fn f64_via_value_is_correctly_rounded() {
for s in LITERALS {
let expected: f64 = s.parse().unwrap();
assert_eq!(via_value::<f64>(s).to_bits(), expected.to_bits(), "{s}");
let mut negative = String::from("-");
negative.push_str(s);
let expected: f64 = negative.parse().unwrap();
assert_eq!(
via_value::<f64>(&negative).to_bits(),
expected.to_bits(),
"{negative}"
);
}
}
#[test]
fn f32_via_value_is_correctly_rounded() {
for s in LITERALS {
let expected: f32 = s.parse().unwrap();
assert_eq!(via_value::<f32>(s).to_bits(), expected.to_bits(), "{s}");
}
}
#[test]
fn every_three_decimal_literal_round_trips() {
let mut s = String::new();
for n in 0..=1000u32 {
s.clear();
write!(s, "{}.{:03}", n / 1000, n % 1000).unwrap();
let expected: f64 = s.parse().unwrap();
assert_eq!(via_value::<f64>(&s).to_bits(), expected.to_bits(), "{s}");
}
}
#[test]
fn re_encoding_a_value_preserves_the_literal() {
for s in LITERALS {
let value: Value<Global> = musli::json::from_str(s).unwrap();
let encoded = musli::json::to_string(&value).unwrap();
let expected: f64 = s.parse().unwrap();
let actual: f64 = encoded.parse().unwrap();
assert_eq!(
actual.to_bits(),
expected.to_bits(),
"{s} encoded as {encoded}"
);
}
}
#[cfg(feature = "sqlite-jsonb")]
#[test]
fn jsonb_float_via_value_is_correctly_rounded() {
const FLOAT: u8 = 5;
const FLOAT5: u8 = 6;
fn element(kind: u8, payload: &str) -> Vec<u8> {
let mut out = Vec::new();
if payload.len() <= 11 {
out.push(((payload.len() as u8) << 4) | kind);
} else {
out.push(0xc0 | kind);
out.push(payload.len() as u8);
}
out.extend_from_slice(payload.as_bytes());
out
}
for s in LITERALS {
let expected: f64 = s.parse().unwrap();
let blob = element(FLOAT, s);
let value: Value<Global> = musli::sqlite_jsonb::from_slice(&blob).unwrap();
let actual: f64 = musli::value::decode(&value).unwrap();
assert_eq!(actual.to_bits(), expected.to_bits(), "{s}");
}
for (s, expected) in [(".5", 0.5f64), ("5.", 5.0), ("-.6", -0.6)] {
let blob = element(FLOAT5, s);
let value: Value<Global> = musli::sqlite_jsonb::from_slice(&blob).unwrap();
let actual: f64 = musli::value::decode(&value).unwrap();
assert_eq!(actual.to_bits(), expected.to_bits(), "{s}");
}
}