use oasis_cbor_derive::{Decode, Encode};
use serde::{Deserialize, Serialize};
use crate::{SimpleValue, Value};
macro_rules! str {
($s:expr) => {
Value::TextString($s.to_owned())
};
}
fn assert_compat_roundtrip<T>(v: T, expected_intermediate: Value)
where
T: Clone,
T: serde::Serialize + crate::Decode, T: crate::Encode + serde::de::DeserializeOwned, T: std::cmp::PartialEq + std::fmt::Debug, {
assert_serde_roundtrip(v.clone(), expected_intermediate);
println!(
"oasis_cbor representation of {:?}: {:?}",
v,
v.clone().into_cbor_value(),
);
let intermediate = crate::serde::to_value(&v).unwrap();
assert_eq!(
intermediate,
v.clone().into_cbor_value(),
"intermediate representation (left) does not match oasis_cbor (right)"
);
let bytes = crate::to_vec(v.clone());
let reconstructed: T = crate::serde::from_slice(&bytes).expect("serde decoding should succeed");
assert_eq!(
v, reconstructed,
"bad round-trip when encoding with oasis_cbor and decoding with serde"
);
let bytes = crate::serde::to_vec(&v).unwrap();
let reconstructed: T = crate::from_slice(&bytes).expect("oasis_cbor decoding should succeed");
assert_eq!(
v, reconstructed,
"bad round-trip when encoding with serde and decoding with oasis_cbor"
);
}
fn assert_serde_roundtrip<T>(v: T, expected_intermediate: Value)
where
T: serde::Serialize + serde::de::DeserializeOwned, T: std::cmp::PartialEq + std::fmt::Debug, {
let intermediate = crate::serde::to_value(&v).unwrap();
println!("serde representation of {:?}: {:?}", v, intermediate);
assert_eq!(
intermediate, expected_intermediate,
"intermediate representation (left) does not match expected value (right)"
);
let bytes = crate::serde::to_vec(&v).unwrap();
let reconstructed: T = crate::serde::from_slice(&bytes).expect("serde decoding should succeed");
assert_eq!(
v, reconstructed,
"bad round-trip when (de)serializing with oasis_cbor::serde"
);
}
#[test]
fn test_simple_types() {
assert_compat_roundtrip(false, Value::Simple(SimpleValue::FalseValue));
assert_compat_roundtrip(0u64, Value::Unsigned(0));
assert_compat_roundtrip(1_000_000i32, Value::Unsigned(1_000_000));
assert_compat_roundtrip(1_000_000i64, Value::Unsigned(1_000_000));
assert_compat_roundtrip(0u128, Value::ByteString(vec![]));
assert_compat_roundtrip(1_000_000u128, Value::ByteString(vec![15, 66, 64]));
assert_compat_roundtrip('A', Value::Unsigned(65));
assert_compat_roundtrip("foo".to_string(), str!("foo"));
}
#[test]
fn test_float() {
let err = crate::serde::to_value(&1.0)
.err()
.expect("encoding f32 should fail");
assert!(
matches!(err, crate::serde::Error::UnsupportedType(_)),
"f32 should be marked as unsupported"
);
let one = [0xf9, 0x3c, 0x00]; let err = crate::serde::from_slice::<f32>(&one)
.err()
.expect("decoding f32 should fail");
assert!(
matches!(
err,
crate::serde::Error::ByteDecoder(
crate::reader::DecoderError::UnsupportedFloatingPointValue
)
),
"f32 should be marked as unsupported, but error was {:?}",
err
);
}
#[test]
fn test_vec() {
assert_compat_roundtrip(
vec![30u16, 10],
Value::Array(vec![Value::Unsigned(30), Value::Unsigned(10)]),
);
}
#[test]
fn test_tuple() {
assert_compat_roundtrip(
(101, 102, 103, 104, 105, 106, 107, 108, 109, 110),
Value::Array((101..=110).map(Value::Unsigned).collect::<Vec<_>>()),
);
assert_compat_roundtrip(
(1, "one".to_string(), ()),
Value::Array(vec![
Value::Unsigned(1),
str!("one"),
Value::Simple(SimpleValue::NullValue),
]),
);
}
#[test]
fn test_map() {
let mut m = std::collections::BTreeMap::new();
m.insert("foo".to_string(), "one".to_string());
m.insert("bar".to_string(), "two".to_string());
m.insert("baz".to_string(), "three".to_string());
m.insert("quux".to_string(), "four".to_string());
assert_compat_roundtrip(
m,
Value::Map(vec![
(str!("bar"), str!("two")),
(str!("baz"), str!("three")),
(str!("foo"), str!("one")),
(str!("quux"), str!("four")),
]),
);
let mut m = std::collections::BTreeMap::new();
m.insert(2u8, 4u8);
m.insert(1, 5);
m.insert(3, 3);
assert_compat_roundtrip(
m,
Value::Map(vec![
(Value::Unsigned(1), Value::Unsigned(5)),
(Value::Unsigned(2), Value::Unsigned(4)),
(Value::Unsigned(3), Value::Unsigned(3)),
]),
);
}
#[test]
fn test_bytes() {
assert_serde_roundtrip(
vec![31u8, 11],
Value::Array(vec![Value::Unsigned(31), Value::Unsigned(11)]),
);
let hello_bytes = vec![104, 101, 108, 108, 111];
let wrapped_bytes = serde_bytes::ByteBuf::from(&*hello_bytes);
assert_serde_roundtrip(wrapped_bytes, Value::ByteString(hello_bytes));
}
#[test]
fn test_option() {
let n: Option<u16> = None;
assert_compat_roundtrip(n, Value::Simple(SimpleValue::NullValue));
let s = Some("foo".to_string());
assert_compat_roundtrip(s, str!("foo"));
}
#[test]
fn test_unit() {
let x = ();
assert_compat_roundtrip(x, Value::Simple(SimpleValue::NullValue));
}
mod enums {
use super::*;
#[test]
fn test_unit_variant() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, Clone)]
enum Enum {
One,
Second,
Three,
}
assert_compat_roundtrip(Enum::Second, str!("Second"));
}
#[test]
fn test_newtype_variant() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, Clone)]
enum E {
M(String),
N(u8),
}
assert_compat_roundtrip(E::N(10), Value::Map(vec![(str!("N"), Value::Unsigned(10))]));
}
#[test]
fn test_tuple_variant() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, Clone)]
enum E {
M(String, u16),
N(u8),
}
assert_compat_roundtrip(
E::M("foo".to_string(), 10),
Value::Map(vec![(
str!("M"),
Value::Array(vec![str!("foo"), Value::Unsigned(10)]),
)]),
);
}
#[test]
fn test_struct_variant() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, Clone)]
enum E {
S { r: u8, g: u8, b: u8 },
}
assert_compat_roundtrip(
E::S {
r: 10,
g: 20,
b: 30,
},
Value::Map(vec![(
str!("S"),
Value::Map(vec![
(str!("r"), Value::Unsigned(10)),
(str!("g"), Value::Unsigned(20)),
(str!("b"), Value::Unsigned(30)),
]),
)]),
);
}
#[test]
fn test_explicit_discriminants() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, Clone)]
enum E {
Two = 2,
Three,
}
assert_serde_roundtrip(E::Two, str!("Two"));
assert_compat_roundtrip(E::Three, str!("Three"));
}
}
mod structs {
use super::*;
#[test]
fn test_newtype_struct() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, Clone, Default)]
struct Millimeters(u16);
assert_compat_roundtrip(Millimeters(100), Value::Array(vec![Value::Unsigned(100)]));
}
#[test]
fn test_tuple_struct() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, Clone, Default)]
struct Rgb(u8, u8, u8);
assert_compat_roundtrip(
Rgb(10, 20, 30),
Value::Array(vec![
Value::Unsigned(10),
Value::Unsigned(20),
Value::Unsigned(30),
]),
);
}
#[test]
fn test_classic_struct() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, Clone, Default)]
struct Color {
r: u8,
g: u8,
b: u8,
}
assert_compat_roundtrip(
Color {
r: 10,
g: 20,
b: 30,
},
Value::Map(vec![
(str!("r"), Value::Unsigned(10)),
(str!("g"), Value::Unsigned(20)),
(str!("b"), Value::Unsigned(30)),
]),
);
}
#[test]
fn test_unit_struct() {
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Encode, Decode, Clone, Default)]
struct Unit;
let v = Unit {};
assert_compat_roundtrip(v, Value::Simple(SimpleValue::NullValue));
}
}