use base64::{Engine, prelude::BASE64_URL_SAFE};
use serde::{
de,
ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
};
use std::collections::BTreeMap;
use crate::{FieldKey, FieldValue, Json};
const I64_KEY_PREFIX: &str = "i64:";
const B64_PREFIX: &str = "b64:";
const TXT_PREFIX: &str = "txt:";
fn needs_txt_escape(text: &str) -> bool {
text.starts_with(B64_PREFIX) || text.starts_with(TXT_PREFIX) || text.starts_with(I64_KEY_PREFIX)
}
struct JsonEscaped<'a>(&'a Json);
impl Serialize for JsonEscaped<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self.0 {
Json::String(s) if needs_txt_escape(s) => {
format!("{TXT_PREFIX}{s}").serialize(serializer)
}
Json::Array(arr) => {
let mut seq = serializer.serialize_seq(Some(arr.len()))?;
for v in arr {
seq.serialize_element(&JsonEscaped(v))?;
}
seq.end()
}
Json::Object(obj) => {
let mut map = serializer.serialize_map(Some(obj.len()))?;
for (k, v) in obj {
if needs_txt_escape(k) {
map.serialize_entry(&format!("{TXT_PREFIX}{k}"), &JsonEscaped(v))?;
} else {
map.serialize_entry(k, &JsonEscaped(v))?;
}
}
map.end()
}
v => v.serialize(serializer),
}
}
}
impl Serialize for FieldKey {
#[inline]
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
FieldKey::Text(x) => {
if serializer.is_human_readable() && needs_txt_escape(x) {
format!("{TXT_PREFIX}{x}").serialize(serializer)
} else {
serializer.serialize_str(x)
}
}
FieldKey::I64(x) => {
if serializer.is_human_readable() {
format!("{I64_KEY_PREFIX}{x}").serialize(serializer)
} else {
serializer.serialize_i64(*x)
}
}
FieldKey::Bytes(x) => {
if serializer.is_human_readable() {
format!("{B64_PREFIX}{}", BASE64_URL_SAFE.encode(x)).serialize(serializer)
} else {
serializer.serialize_bytes(x)
}
}
}
}
}
impl<'de> de::Deserialize<'de> for FieldKey {
#[inline]
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::Error;
let is_human_readable = deserializer.is_human_readable();
let val = deserializer.deserialize_any(KeyVisitor)?;
if is_human_readable && let FieldKey::Text(x) = &val {
if let Some(raw) = x.strip_prefix(I64_KEY_PREFIX) {
let i = raw.parse::<i64>().map_err(|err| {
D::Error::custom(format!("invalid {I64_KEY_PREFIX:?} key {x:?}: {err}"))
})?;
return Ok(FieldKey::I64(i));
}
if let Some(raw) = x.strip_prefix(B64_PREFIX) {
let decoded = BASE64_URL_SAFE.decode(raw).map_err(|err| {
D::Error::custom(format!("invalid {B64_PREFIX:?} key {x:?}: {err}"))
})?;
return Ok(FieldKey::Bytes(decoded));
}
if let Some(raw) = x.strip_prefix(TXT_PREFIX) {
return Ok(FieldKey::Text(raw.to_string()));
}
}
Ok(val)
}
}
impl Serialize for FieldValue {
#[inline]
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
FieldValue::Bool(x) => serializer.serialize_bool(*x),
FieldValue::I64(x) => serializer.serialize_i64(*x),
FieldValue::U64(x) => serializer.serialize_u64(*x),
FieldValue::F64(x) => {
if x.is_nan() {
return Err(serde::ser::Error::custom("cannot serialize NaN F64"));
}
serializer.serialize_f64(*x)
}
FieldValue::F32(x) => {
if x.is_nan() {
return Err(serde::ser::Error::custom("cannot serialize NaN F32"));
}
serializer.serialize_f32(*x)
}
FieldValue::Bytes(x) => {
if serializer.is_human_readable() {
format!("{B64_PREFIX}{}", BASE64_URL_SAFE.encode(x)).serialize(serializer)
} else {
serializer.serialize_bytes(x)
}
}
FieldValue::Text(x) => {
if serializer.is_human_readable() && needs_txt_escape(x) {
format!("{TXT_PREFIX}{x}").serialize(serializer)
} else {
serializer.serialize_str(x)
}
}
FieldValue::Json(x) => {
if serializer.is_human_readable() {
JsonEscaped(x).serialize(serializer)
} else {
x.serialize(serializer)
}
}
FieldValue::Null => serializer.serialize_unit(),
FieldValue::Vector(x) => {
let mut seq = serializer.serialize_seq(Some(x.len()))?;
for v in x {
seq.serialize_element(&v.to_bits())?;
}
seq.end()
}
FieldValue::Array(x) => {
let mut seq = serializer.serialize_seq(Some(x.len()))?;
for v in x {
seq.serialize_element(v)?;
}
seq.end()
}
FieldValue::Map(x) => {
let mut map = serializer.serialize_map(Some(x.len()))?;
for (k, v) in x {
map.serialize_entry(k, v)?;
}
map.end()
}
}
}
}
impl<'de> de::Deserialize<'de> for FieldValue {
#[inline]
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::Error;
let is_human_readable = deserializer.is_human_readable();
let val = deserializer.deserialize_any(Visitor)?;
if is_human_readable && let FieldValue::Text(x) = &val {
if let Some(raw) = x.strip_prefix(B64_PREFIX) {
let decoded = BASE64_URL_SAFE.decode(raw).map_err(|err| {
D::Error::custom(format!("invalid {B64_PREFIX:?} value {x:?}: {err}"))
})?;
return Ok(FieldValue::Bytes(decoded));
}
if let Some(raw) = x.strip_prefix(TXT_PREFIX) {
return Ok(FieldValue::Text(raw.to_string()));
}
}
Ok(val)
}
}
struct KeyVisitor;
impl<'de> de::Visitor<'de> for KeyVisitor {
type Value = FieldKey;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(formatter, "string, signed integer or bytes")
}
#[inline]
fn visit_i8<E: de::Error>(self, v: i8) -> Result<Self::Value, E> {
Ok(FieldKey::I64(v.into()))
}
#[inline]
fn visit_i16<E: de::Error>(self, v: i16) -> Result<Self::Value, E> {
Ok(FieldKey::I64(v.into()))
}
#[inline]
fn visit_i32<E: de::Error>(self, v: i32) -> Result<Self::Value, E> {
Ok(FieldKey::I64(v.into()))
}
#[inline]
fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
Ok(FieldKey::I64(v))
}
#[inline]
fn visit_i128<E: de::Error>(self, v: i128) -> Result<Self::Value, E> {
Ok(FieldKey::I64(
i64::try_from(v).map_err(|_| de::Error::custom("i128 overflow"))?,
))
}
#[inline]
fn visit_u8<E: de::Error>(self, v: u8) -> Result<Self::Value, E> {
Ok(FieldKey::I64(v.into()))
}
#[inline]
fn visit_u16<E: de::Error>(self, v: u16) -> Result<Self::Value, E> {
Ok(FieldKey::I64(v.into()))
}
#[inline]
fn visit_u32<E: de::Error>(self, v: u32) -> Result<Self::Value, E> {
Ok(FieldKey::I64(v.into()))
}
#[inline]
fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
Ok(FieldKey::I64(
i64::try_from(v).map_err(|_| de::Error::custom("u64 overflow"))?,
))
}
#[inline]
fn visit_u128<E: de::Error>(self, v: u128) -> Result<Self::Value, E> {
Ok(FieldKey::I64(
i64::try_from(v).map_err(|_| de::Error::custom("u128 overflow"))?,
))
}
#[inline]
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
Ok(FieldKey::Text(v.into()))
}
#[inline]
fn visit_borrowed_str<E: de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
Ok(FieldKey::Text(v.into()))
}
#[inline]
fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
Ok(FieldKey::Text(v))
}
#[inline]
fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
Ok(FieldKey::Bytes(v.to_vec()))
}
#[inline]
fn visit_borrowed_bytes<E: de::Error>(self, v: &'de [u8]) -> Result<Self::Value, E> {
Ok(FieldKey::Bytes(v.to_vec()))
}
#[inline]
fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
Ok(FieldKey::Bytes(v))
}
#[inline]
fn visit_seq<A: de::SeqAccess<'de>>(self, mut acc: A) -> Result<Self::Value, A::Error> {
let mut seq: Vec<u8> =
Vec::with_capacity(acc.size_hint().filter(|&l| l < 1024).unwrap_or(0));
while let Some(elem) = acc.next_element()? {
seq.push(elem);
}
Ok(FieldKey::Bytes(seq))
}
}
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = FieldValue;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(formatter, "a valid CBOR item")
}
#[inline]
fn visit_bool<E: de::Error>(self, v: bool) -> Result<Self::Value, E> {
Ok(FieldValue::Bool(v))
}
#[inline]
fn visit_f32<E: de::Error>(self, v: f32) -> Result<Self::Value, E> {
if v.is_nan() {
return Err(E::custom("cannot deserialize NaN F32"));
}
Ok(FieldValue::F32(v))
}
#[inline]
fn visit_f64<E: de::Error>(self, v: f64) -> Result<Self::Value, E> {
if v.is_nan() {
return Err(E::custom("cannot deserialize NaN F64"));
}
Ok(FieldValue::F64(v))
}
#[inline]
fn visit_i8<E: de::Error>(self, v: i8) -> Result<Self::Value, E> {
Ok(FieldValue::I64(v.into()))
}
#[inline]
fn visit_i16<E: de::Error>(self, v: i16) -> Result<Self::Value, E> {
Ok(FieldValue::I64(v.into()))
}
#[inline]
fn visit_i32<E: de::Error>(self, v: i32) -> Result<Self::Value, E> {
Ok(FieldValue::I64(v.into()))
}
#[inline]
fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
Ok(FieldValue::I64(v))
}
#[inline]
fn visit_i128<E: de::Error>(self, v: i128) -> Result<Self::Value, E> {
Ok(FieldValue::I64(
i64::try_from(v).map_err(|_| de::Error::custom("i128 overflow"))?,
))
}
#[inline]
fn visit_u8<E: de::Error>(self, v: u8) -> Result<Self::Value, E> {
Ok(FieldValue::U64(v.into()))
}
#[inline]
fn visit_u16<E: de::Error>(self, v: u16) -> Result<Self::Value, E> {
Ok(FieldValue::U64(v.into()))
}
#[inline]
fn visit_u32<E: de::Error>(self, v: u32) -> Result<Self::Value, E> {
Ok(FieldValue::U64(v.into()))
}
#[inline]
fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
Ok(FieldValue::U64(v))
}
#[inline]
fn visit_u128<E: de::Error>(self, v: u128) -> Result<Self::Value, E> {
Ok(FieldValue::U64(
u64::try_from(v).map_err(|_| de::Error::custom("u128 overflow"))?,
))
}
#[inline]
fn visit_char<E: de::Error>(self, v: char) -> Result<Self::Value, E> {
Ok(FieldValue::Text(v.into()))
}
#[inline]
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
Ok(FieldValue::Text(v.into()))
}
#[inline]
fn visit_borrowed_str<E: de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
Ok(FieldValue::Text(v.into()))
}
#[inline]
fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
Ok(FieldValue::Text(v))
}
#[inline]
fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
Ok(FieldValue::Bytes(v.to_vec()))
}
#[inline]
fn visit_borrowed_bytes<E: de::Error>(self, v: &'de [u8]) -> Result<Self::Value, E> {
Ok(FieldValue::Bytes(v.to_vec()))
}
#[inline]
fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
Ok(FieldValue::Bytes(v))
}
#[inline]
fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(FieldValue::Null)
}
#[inline]
fn visit_some<D: de::Deserializer<'de>>(
self,
deserializer: D,
) -> Result<Self::Value, D::Error> {
deserializer.deserialize_any(self)
}
#[inline]
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(FieldValue::Null)
}
#[inline]
fn visit_newtype_struct<D: de::Deserializer<'de>>(
self,
deserializer: D,
) -> Result<Self::Value, D::Error> {
deserializer.deserialize_any(self)
}
#[inline]
fn visit_seq<A: de::SeqAccess<'de>>(self, mut acc: A) -> Result<Self::Value, A::Error> {
let mut seq: Vec<FieldValue> =
Vec::with_capacity(acc.size_hint().filter(|&l| l < 1024).unwrap_or(0));
while let Some(elem) = acc.next_element()? {
seq.push(elem);
}
Ok(FieldValue::Array(seq))
}
#[inline]
fn visit_map<A: de::MapAccess<'de>>(self, mut acc: A) -> Result<Self::Value, A::Error> {
let mut map = BTreeMap::<FieldKey, FieldValue>::new();
while let Some((key, value)) = acc.next_entry()? {
if let std::collections::btree_map::Entry::Vacant(e) = map.entry(key) {
e.insert(value);
} else {
return Err(de::Error::custom("duplicate map key"));
}
}
Ok(FieldValue::Map(map))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{FieldKey, FieldValue};
use base64::{Engine, prelude::BASE64_URL_SAFE};
use half::bf16;
use serde::{
Deserialize as _,
de::{IntoDeserializer, Visitor as _},
};
use serde_json::json;
fn cbor_roundtrip<T, U>(value: &T) -> U
where
T: Serialize,
U: for<'de> de::Deserialize<'de>,
{
let mut bytes = Vec::new();
cbor2::to_writer(value, &mut bytes).unwrap();
cbor2::from_reader(bytes.as_slice()).unwrap()
}
#[test]
fn field_key_serde_covers_text_bytes_and_human_readable_prefixes() {
for raw in ["not base64!", "test", "cafe", "1234"] {
let text = FieldKey::Text(raw.into());
let encoded_text = serde_json::to_string(&text).unwrap();
assert_eq!(encoded_text, format!("{raw:?}"));
assert_eq!(
serde_json::from_str::<FieldKey>(&encoded_text).unwrap(),
text
);
}
let i64_key = FieldKey::I64(-7);
let encoded_i64 = serde_json::to_string(&i64_key).unwrap();
assert_eq!(encoded_i64, r#""i64:-7""#);
assert_eq!(
serde_json::from_str::<FieldKey>(&encoded_i64).unwrap(),
i64_key
);
assert_eq!(
serde_json::from_value::<FieldKey>(json!(-7)).unwrap(),
FieldKey::I64(-7)
);
let bytes = FieldKey::Bytes(vec![1, 2, 3]);
let encoded_bytes = serde_json::to_string(&bytes).unwrap();
assert_eq!(encoded_bytes, r#""b64:AQID""#);
assert_eq!(
serde_json::from_str::<FieldKey>(&encoded_bytes).unwrap(),
bytes
);
for raw in ["i64:-7", "b64:AQID", "txt:hello", "i64:not-an-int"] {
let text = FieldKey::Text(raw.into());
let encoded = serde_json::to_string(&text).unwrap();
assert_eq!(encoded, format!("\"txt:{raw}\""));
assert_eq!(serde_json::from_str::<FieldKey>(&encoded).unwrap(), text);
}
assert!(serde_json::from_value::<FieldKey>(json!("i64:abc")).is_err());
assert!(serde_json::from_value::<FieldKey>(json!("b64:!!!")).is_err());
assert_eq!(
cbor_roundtrip::<_, FieldKey>(&FieldKey::Bytes(vec![4, 5])),
FieldKey::Bytes(vec![4, 5])
);
assert_eq!(
cbor_roundtrip::<_, FieldKey>(&FieldKey::I64(42)),
FieldKey::I64(42)
);
assert_eq!(
cbor_roundtrip::<_, FieldKey>(&FieldKey::Text("i64:-7".into())),
FieldKey::Text("i64:-7".into())
);
assert_eq!(
serde_json::from_value::<FieldKey>(json!([7, 8])).unwrap(),
FieldKey::Bytes(vec![7, 8])
);
}
#[test]
fn field_value_text_json_roundtrip_never_corrupts() {
for raw in [
"test",
"cafe",
"1234",
"AQID",
"hello world",
"",
"b64:AQID",
"txt:x",
"i64:5",
] {
let value = FieldValue::Text(raw.into());
let encoded = serde_json::to_string(&value).unwrap();
assert_eq!(
serde_json::from_str::<FieldValue>(&encoded).unwrap(),
value,
"JSON round-trip corrupted {raw:?} (encoded as {encoded})"
);
}
let bytes = FieldValue::Bytes(b"test".to_vec());
let encoded = serde_json::to_string(&bytes).unwrap();
assert_eq!(encoded, r#""b64:dGVzdA==""#);
assert_eq!(serde_json::from_str::<FieldValue>(&encoded).unwrap(), bytes);
assert!(serde_json::from_value::<FieldValue>(json!("b64:!!!")).is_err());
}
#[test]
fn field_value_serializes_all_variants_and_decodes_human_readable_bytes() {
assert_eq!(
serde_json::to_value(FieldValue::Bool(true)).unwrap(),
json!(true)
);
assert_eq!(
serde_json::to_value(FieldValue::I64(-1)).unwrap(),
json!(-1)
);
assert_eq!(serde_json::to_value(FieldValue::U64(1)).unwrap(), json!(1));
assert_eq!(
serde_json::to_value(FieldValue::F64(1.5)).unwrap(),
json!(1.5)
);
assert_eq!(
serde_json::to_value(FieldValue::F32(1.25)).unwrap(),
json!(1.25)
);
assert_eq!(
serde_json::to_value(FieldValue::Text("not base64!".into())).unwrap(),
json!("not base64!")
);
assert_eq!(
serde_json::to_value(FieldValue::Json(json!({"a": 1}))).unwrap(),
json!({"a": 1})
);
assert_eq!(
serde_json::to_value(FieldValue::Null).unwrap(),
serde_json::Value::Null
);
assert_eq!(
serde_json::to_value(FieldValue::Vector(vec![bf16::from_f32(1.0)])).unwrap(),
json!([bf16::from_f32(1.0).to_bits()])
);
assert_eq!(
serde_json::to_value(FieldValue::Array(vec![FieldValue::U64(1)])).unwrap(),
json!([1])
);
let encoded = serde_json::to_string(&FieldValue::Bytes(vec![1, 2, 3])).unwrap();
assert_eq!(encoded, r#""b64:AQID""#);
assert_eq!(
serde_json::from_str::<FieldValue>(&encoded).unwrap(),
FieldValue::Bytes(vec![1, 2, 3])
);
assert_eq!(
serde_json::from_value::<FieldValue>(json!("not base64!")).unwrap(),
FieldValue::Text("not base64!".into())
);
let mut map = BTreeMap::new();
map.insert(FieldKey::Text("not base64!".into()), FieldValue::Bool(true));
map.insert(FieldKey::I64(-7), FieldValue::Text("seven".into()));
map.insert(FieldKey::Bytes(vec![9]), FieldValue::U64(9));
let json_value = serde_json::to_value(FieldValue::Map(map.clone())).unwrap();
assert_eq!(json_value["not base64!"], json!(true));
assert_eq!(json_value["i64:-7"], json!("seven"));
assert_eq!(
json_value[format!("b64:{}", BASE64_URL_SAFE.encode([9]))],
json!(9)
);
let decoded: FieldValue = serde_json::from_value(json_value).unwrap();
assert_eq!(decoded, FieldValue::Map(map));
}
#[test]
fn field_value_json_escapes_reserved_prefixes_in_human_readable() {
let val = FieldValue::Json(json!({
"plain": "hello",
"b64:key": "b64:AQID",
"nested": ["txt:x", {"i64:5": "i64:7"}],
}));
let encoded = serde_json::to_string(&val).unwrap();
assert!(encoded.contains("txt:b64:AQID"), "encoded: {encoded}");
assert!(encoded.contains("txt:b64:key"), "encoded: {encoded}");
let decoded: FieldValue = serde_json::from_str(&encoded).unwrap();
let expected = FieldValue::Map(BTreeMap::from([
(
FieldKey::Text("plain".into()),
FieldValue::Text("hello".into()),
),
(
FieldKey::Text("b64:key".into()),
FieldValue::Text("b64:AQID".into()),
),
(
FieldKey::Text("nested".into()),
FieldValue::Array(vec![
FieldValue::Text("txt:x".into()),
FieldValue::Map(BTreeMap::from([(
FieldKey::Text("i64:5".into()),
FieldValue::Text("i64:7".into()),
)])),
]),
),
]));
assert_eq!(decoded, expected);
let val = FieldValue::Json(json!("b64:AQID"));
let encoded = serde_json::to_string(&val).unwrap();
assert_eq!(encoded, r#""txt:b64:AQID""#);
assert_eq!(
serde_json::from_str::<FieldValue>(&encoded).unwrap(),
FieldValue::Text("b64:AQID".into())
);
assert_eq!(
serde_json::to_value(FieldValue::Json(json!({"a": [1, true, null, "x"]}))).unwrap(),
json!({"a": [1, true, null, "x"]})
);
let val = FieldValue::Json(json!({"k": "b64:AQID"}));
let mut bytes = Vec::new();
cbor2::to_writer(&val, &mut bytes).unwrap();
let decoded: FieldValue = cbor2::from_reader(bytes.as_slice()).unwrap();
assert_eq!(
decoded,
FieldValue::Map(BTreeMap::from([(
FieldKey::Text("k".into()),
FieldValue::Text("b64:AQID".into()),
)]))
);
}
#[test]
fn field_value_map_rejects_duplicate_keys() {
let err = serde_json::from_str::<FieldValue>(r#"{"k":1,"k":2}"#).unwrap_err();
assert!(err.to_string().contains("duplicate map key"));
let dup_map = cbor2::Value::Map(vec![
(
cbor2::Value::Text("k".into()),
cbor2::Value::Integer(1.into()),
),
(
cbor2::Value::Text("k".into()),
cbor2::Value::Integer(2.into()),
),
]);
let mut bytes = Vec::new();
cbor2::to_writer(&dup_map, &mut bytes).unwrap();
let err = cbor2::from_reader::<FieldValue, _>(bytes.as_slice()).unwrap_err();
assert!(err.to_string().contains("duplicate map key"));
}
#[test]
fn field_value_deserialize_visitors_cover_numbers_arrays_maps_and_errors() {
assert_eq!(
serde_json::from_value::<FieldValue>(json!(true)).unwrap(),
FieldValue::Bool(true)
);
assert_eq!(
serde_json::from_value::<FieldValue>(json!(-5)).unwrap(),
FieldValue::I64(-5)
);
assert_eq!(
serde_json::from_value::<FieldValue>(json!(5)).unwrap(),
FieldValue::U64(5)
);
assert_eq!(
serde_json::from_value::<FieldValue>(json!(1.5)).unwrap(),
FieldValue::F64(1.5)
);
assert_eq!(
serde_json::from_value::<FieldValue>(serde_json::Value::Null).unwrap(),
FieldValue::Null
);
assert_eq!(
serde_json::from_value::<FieldValue>(json!([1, "not base64!"])).unwrap(),
FieldValue::Array(vec![
FieldValue::U64(1),
FieldValue::Text("not base64!".into())
])
);
let value = cbor_roundtrip::<_, FieldValue>(&vec![1u8, 2u8, 3u8]);
assert_eq!(
value,
FieldValue::Array(vec![
FieldValue::U64(1),
FieldValue::U64(2),
FieldValue::U64(3)
])
);
assert_eq!(
cbor_roundtrip::<_, FieldValue>(&serde_bytes::ByteBuf::from(vec![1, 2])),
FieldValue::Bytes(vec![1, 2])
);
assert_eq!(
cbor_roundtrip::<_, FieldValue>(&'x'),
FieldValue::Text("x".into())
);
assert_eq!(
cbor_roundtrip::<_, FieldValue>(&1.25f32),
FieldValue::F64(1.25)
);
let f32_deserializer: serde::de::value::F32Deserializer<serde::de::value::Error> =
1.25f32.into_deserializer();
assert_eq!(
FieldValue::deserialize(f32_deserializer).unwrap(),
FieldValue::F32(1.25)
);
let i128_deserializer: serde::de::value::I128Deserializer<serde::de::value::Error> =
(i128::from(i64::MAX) + 1).into_deserializer();
let result = FieldValue::deserialize(i128_deserializer);
let err = result.unwrap_err();
assert!(err.to_string().contains("i128 overflow"));
let u128_deserializer: serde::de::value::U128Deserializer<serde::de::value::Error> =
(u128::from(u64::MAX) + 1).into_deserializer();
let result = FieldValue::deserialize(u128_deserializer);
let err = result.unwrap_err();
assert!(err.to_string().contains("u128 overflow"));
assert!(serde_json::from_str::<FieldValue>("NaN").is_err());
assert!(serde_json::to_string(&FieldValue::F64(f64::NAN)).is_err());
assert!(serde_json::to_string(&FieldValue::F32(f32::NAN)).is_err());
}
#[test]
fn visitors_directly_cover_narrow_numeric_bytes_and_option_paths() {
type DeError = serde::de::value::Error;
assert_eq!(
KeyVisitor.visit_borrowed_bytes::<DeError>(b"key").unwrap(),
FieldKey::Bytes(b"key".to_vec())
);
assert_eq!(
KeyVisitor.visit_i8::<DeError>(-8).unwrap(),
FieldKey::I64(-8)
);
assert_eq!(
KeyVisitor.visit_i16::<DeError>(-16).unwrap(),
FieldKey::I64(-16)
);
assert_eq!(
KeyVisitor.visit_i32::<DeError>(-32).unwrap(),
FieldKey::I64(-32)
);
assert_eq!(
KeyVisitor.visit_i64::<DeError>(-64).unwrap(),
FieldKey::I64(-64)
);
assert_eq!(KeyVisitor.visit_u8::<DeError>(8).unwrap(), FieldKey::I64(8));
assert_eq!(
KeyVisitor.visit_u16::<DeError>(16).unwrap(),
FieldKey::I64(16)
);
assert_eq!(
KeyVisitor.visit_u32::<DeError>(32).unwrap(),
FieldKey::I64(32)
);
assert_eq!(
KeyVisitor.visit_u64::<DeError>(64).unwrap(),
FieldKey::I64(64)
);
assert!(KeyVisitor.visit_i128::<DeError>(i128::MAX).is_err());
assert!(KeyVisitor.visit_u128::<DeError>(u128::MAX).is_err());
assert_eq!(
KeyVisitor
.visit_byte_buf::<DeError>(b"owned-key".to_vec())
.unwrap(),
FieldKey::Bytes(b"owned-key".to_vec())
);
assert_eq!(
Visitor.visit_i8::<DeError>(-8).unwrap(),
FieldValue::I64(-8)
);
assert_eq!(
Visitor.visit_i16::<DeError>(-16).unwrap(),
FieldValue::I64(-16)
);
assert_eq!(
Visitor.visit_i32::<DeError>(-32).unwrap(),
FieldValue::I64(-32)
);
assert_eq!(Visitor.visit_u8::<DeError>(8).unwrap(), FieldValue::U64(8));
assert_eq!(
Visitor.visit_u16::<DeError>(16).unwrap(),
FieldValue::U64(16)
);
assert_eq!(
Visitor.visit_u32::<DeError>(32).unwrap(),
FieldValue::U64(32)
);
assert_eq!(
Visitor.visit_char::<DeError>('x').unwrap(),
FieldValue::Text("x".to_string())
);
assert_eq!(
Visitor.visit_borrowed_bytes::<DeError>(b"value").unwrap(),
FieldValue::Bytes(b"value".to_vec())
);
assert_eq!(
Visitor
.visit_byte_buf::<DeError>(b"owned-value".to_vec())
.unwrap(),
FieldValue::Bytes(b"owned-value".to_vec())
);
assert_eq!(Visitor.visit_none::<DeError>().unwrap(), FieldValue::Null);
assert_eq!(Visitor.visit_unit::<DeError>().unwrap(), FieldValue::Null);
let some_deserializer: serde::de::value::U8Deserializer<DeError> = 42u8.into_deserializer();
assert_eq!(
Visitor.visit_some(some_deserializer).unwrap(),
FieldValue::U64(42)
);
let newtype_deserializer: serde::de::value::StrDeserializer<DeError> =
"nested".into_deserializer();
assert_eq!(
Visitor.visit_newtype_struct(newtype_deserializer).unwrap(),
FieldValue::Text("nested".to_string())
);
assert!(Visitor.visit_i128::<DeError>(i128::MAX).is_err());
assert!(Visitor.visit_u128::<DeError>(u128::MAX).is_err());
}
}