use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use crate::codec::{
self, CodecError, DataFormat, DataHeader, Decodable, Encodable, Format, ReadsDecodable,
UnexpectedDataFormatSnafu, UnsupportedDataFormatSnafu, WritesEncodable,
};
use super::{Text, Type};
#[derive(Default, Debug, Clone, PartialEq)]
pub enum Unspecified {
#[default]
Default,
U8(u8),
I8(i8),
U16(u16),
I16(i16),
U32(u32),
I32(i32),
U64(u64),
I64(i64),
F32(f32),
F64(f64),
Bool(bool),
Text(Text),
List(Vec<Unspecified>),
Map(BTreeMap<Text, Unspecified>),
Data {
header: DataHeader,
raw: Vec<u8>,
},
}
impl Unspecified {
pub const DATA_TYPE: super::DataType = super::DataType::new_fluid(
Text::from("Unspecified"),
Some(Text::from("Unspecified data.")),
);
pub fn as_type(&self) -> Type {
match self {
Unspecified::Default => Type::Unspecified,
Unspecified::U8(_) => Type::U8,
Unspecified::I8(_) => Type::I8,
Unspecified::U16(_) => Type::U16,
Unspecified::I16(_) => Type::I16,
Unspecified::U32(_) => Type::U32,
Unspecified::I32(_) => Type::I32,
Unspecified::U64(_) => Type::U64,
Unspecified::I64(_) => Type::I64,
Unspecified::F32(_) => Type::F32,
Unspecified::F64(_) => Type::F64,
Unspecified::Bool(_) => Type::Bool,
Unspecified::Text(_) => Type::Text,
Unspecified::List(_) => Type::List(alloc::boxed::Box::new(Type::Unspecified)),
Unspecified::Map(_) => {
Type::Map(alloc::boxed::Box::new((Type::Text, Type::Unspecified)))
}
Unspecified::Data { .. } => Type::Unspecified,
}
}
fn homogeneous_ordinal(items: &[Unspecified]) -> Option<u8> {
let first = items.first()?;
let ordinal = first.type_ordinal();
if items[1..].iter().all(|item| item.type_ordinal() == ordinal) {
Some(ordinal)
} else {
None
}
}
pub fn default_of(typing: &Type) -> Unspecified {
match typing {
Type::Unspecified => Unspecified::Default,
Type::U8 => Unspecified::U8(0),
Type::I8 => Unspecified::I8(0),
Type::U16 => Unspecified::U16(0),
Type::I16 => Unspecified::I16(0),
Type::U32 => Unspecified::U32(0),
Type::I32 => Unspecified::I32(0),
Type::U64 => Unspecified::U64(0),
Type::I64 => Unspecified::I64(0),
Type::F32 => Unspecified::F32(0.0),
Type::F64 => Unspecified::F64(0.0),
Type::Bool => Unspecified::Bool(false),
Type::Text => Unspecified::Text(Text::default()),
Type::Data(typing) => Unspecified::Data {
header: DataHeader {
count: 0,
format: typing.format().as_data_format(),
},
raw: Vec::new(),
},
Type::List(_) => Unspecified::List(Vec::new()),
Type::Map(_) => Unspecified::Map(BTreeMap::new()),
}
}
fn type_ordinal(&self) -> u8 {
match self {
Unspecified::Text(_) => 244,
Unspecified::List(_) => 243,
Unspecified::Map(_) => 242,
Unspecified::Data { header, .. } => header.format.ordinal,
_ => self.as_type().ordinal(),
}
}
fn scalar_blob_size(&self) -> u16 {
match self {
Unspecified::U8(_) | Unspecified::I8(_) | Unspecified::Bool(_) => 1,
Unspecified::U16(_) | Unspecified::I16(_) => 2,
Unspecified::U32(_) | Unspecified::I32(_) | Unspecified::F32(_) => 4,
Unspecified::U64(_) | Unspecified::I64(_) | Unspecified::F64(_) => 8,
_ => 0,
}
}
}
impl Encodable for Unspecified {
const FORMAT: Format = Format::Fluid;
fn encode(&self, writer: &mut (impl WritesEncodable + ?Sized)) -> Result<(), CodecError> {
match self {
Unspecified::Default => Ok(()),
Unspecified::U8(v) => v.encode(writer),
Unspecified::I8(v) => v.encode(writer),
Unspecified::U16(v) => v.encode(writer),
Unspecified::I16(v) => v.encode(writer),
Unspecified::U32(v) => v.encode(writer),
Unspecified::I32(v) => v.encode(writer),
Unspecified::U64(v) => v.encode(writer),
Unspecified::I64(v) => v.encode(writer),
Unspecified::F32(v) => v.encode(writer),
Unspecified::F64(v) => v.encode(writer),
Unspecified::Bool(v) => v.encode(writer),
Unspecified::Text(v) => v.encode(writer),
Unspecified::List(items) => encode_unspecified_list(items, writer),
Unspecified::Map(map) => {
let keys: Vec<Unspecified> =
map.keys().map(|k| Unspecified::Text(k.clone())).collect();
let values: Vec<Unspecified> = map.values().cloned().collect();
encode_unspecified_list(&keys, writer)?;
encode_unspecified_list(&values, writer)?;
Ok(())
}
Unspecified::Data { raw, .. } => {
writer.write_all(raw)?;
Ok(())
}
}
}
fn encode_header(
&self,
writer: &mut (impl WritesEncodable + ?Sized),
) -> Result<(), CodecError> {
match self {
Unspecified::Default => DataHeader::default().encode(writer),
Unspecified::U8(_)
| Unspecified::I8(_)
| Unspecified::U16(_)
| Unspecified::I16(_)
| Unspecified::U32(_)
| Unspecified::I32(_)
| Unspecified::U64(_)
| Unspecified::I64(_)
| Unspecified::F32(_)
| Unspecified::F64(_)
| Unspecified::Bool(_) => DataHeader {
count: 1,
format: DataFormat {
blob_size: self.scalar_blob_size(),
data_fields: 0,
ordinal: self.type_ordinal(),
},
}
.encode(writer),
Unspecified::Text(v) => DataHeader {
count: codec::try_count(v.len())?,
format: DataFormat {
blob_size: 1,
data_fields: 0,
ordinal: self.type_ordinal(),
},
}
.encode(writer),
Unspecified::List(_) => DataHeader {
count: 1,
format: DataFormat {
blob_size: 0,
data_fields: 1,
ordinal: self.type_ordinal(),
},
}
.encode(writer),
Unspecified::Map(_) => DataHeader {
count: 1,
format: DataFormat {
blob_size: 0,
data_fields: 2,
ordinal: self.type_ordinal(),
},
}
.encode(writer),
Unspecified::Data { header, .. } => header.encode(writer),
}
}
}
fn encode_unspecified_list(
items: &[Unspecified],
writer: &mut (impl WritesEncodable + ?Sized),
) -> Result<(), CodecError> {
let count = codec::try_count(items.len())?;
if items.iter().any(|i| matches!(i, Unspecified::Default)) {
return UnsupportedDataFormatSnafu { ordinal: 0u8 }.fail();
}
match Unspecified::homogeneous_ordinal(items) {
Some(ordinal) => {
let first = &items[0];
let blob_size = first.scalar_blob_size();
let inner_format = if blob_size > 0 {
DataFormat {
blob_size,
data_fields: 0,
ordinal,
}
} else {
DataFormat {
blob_size: 0,
data_fields: 1,
ordinal,
}
};
DataHeader {
count,
format: inner_format,
}
.encode(writer)?;
if inner_format.data_fields == 0 {
for item in items {
item.encode(writer)?;
}
} else {
for item in items {
writer.write_data(item)?;
}
}
}
None => {
DataHeader {
count,
format: DataFormat {
blob_size: 0,
data_fields: 1,
ordinal: 0,
},
}
.encode(writer)?;
for item in items {
writer.write_data(item)?;
}
}
}
Ok(())
}
fn expected_scalar_blob_size(ordinal: u8) -> Option<u16> {
match Type::from_ordinal(ordinal)? {
Type::U8 | Type::I8 | Type::Bool => Some(1),
Type::U16 | Type::I16 => Some(2),
Type::U32 | Type::I32 | Type::F32 => Some(4),
Type::U64 | Type::I64 | Type::F64 => Some(8),
_ => None,
}
}
fn validate_scalar_format(format: DataFormat) -> Result<(), CodecError> {
if let Some(expected) = expected_scalar_blob_size(format.ordinal) {
if format.data_fields != 0 || format.blob_size != expected {
return Err(UnexpectedDataFormatSnafu {
expected: Format::Blob(expected),
actual: Some(DataHeader { count: 0, format }),
}
.build());
}
}
Ok(())
}
fn decode_unspecified_list(
reader: &mut (impl ReadsDecodable + ?Sized),
) -> Result<Vec<Unspecified>, CodecError> {
let inner: DataHeader = reader.read_data()?;
validate_scalar_format(inner.format)?;
let count = inner.count as usize;
let mut items = Vec::with_capacity(count.min(1024));
match Type::from_ordinal(inner.format.ordinal) {
Some(Type::Unspecified) if inner.format.data_fields == 0 && count > 0 => {
return UnsupportedDataFormatSnafu {
ordinal: inner.format.ordinal,
}
.fail();
}
Some(Type::Unspecified) if inner.format.data_fields == 0 => {}
Some(Type::Text | Type::List(_) | Type::Map(_))
if inner.format.data_fields == 0 && count > 0 =>
{
return UnsupportedDataFormatSnafu {
ordinal: inner.format.ordinal,
}
.fail();
}
Some(Type::U8) => {
for _ in 0..count {
let mut v = 0u8;
v.decode(reader, None)?;
items.push(Unspecified::U8(v));
}
}
Some(Type::U16) => {
for _ in 0..count {
let mut v = 0u16;
v.decode(reader, None)?;
items.push(Unspecified::U16(v));
}
}
Some(Type::U32) => {
for _ in 0..count {
let mut v = 0u32;
v.decode(reader, None)?;
items.push(Unspecified::U32(v));
}
}
Some(Type::U64) => {
for _ in 0..count {
let mut v = 0u64;
v.decode(reader, None)?;
items.push(Unspecified::U64(v));
}
}
Some(Type::I8) => {
for _ in 0..count {
let mut v = 0i8;
v.decode(reader, None)?;
items.push(Unspecified::I8(v));
}
}
Some(Type::I16) => {
for _ in 0..count {
let mut v = 0i16;
v.decode(reader, None)?;
items.push(Unspecified::I16(v));
}
}
Some(Type::I32) => {
for _ in 0..count {
let mut v = 0i32;
v.decode(reader, None)?;
items.push(Unspecified::I32(v));
}
}
Some(Type::I64) => {
for _ in 0..count {
let mut v = 0i64;
v.decode(reader, None)?;
items.push(Unspecified::I64(v));
}
}
Some(Type::F32) => {
for _ in 0..count {
let mut v = 0.0f32;
v.decode(reader, None)?;
items.push(Unspecified::F32(v));
}
}
Some(Type::F64) => {
for _ in 0..count {
let mut v = 0.0f64;
v.decode(reader, None)?;
items.push(Unspecified::F64(v));
}
}
Some(Type::Bool) => {
for _ in 0..count {
let mut v = false;
v.decode(reader, None)?;
items.push(Unspecified::Bool(v));
}
}
_ => {
for _ in 0..count {
let mut item = Unspecified::Default;
reader.read_data_into(&mut item)?;
items.push(item);
}
}
}
Ok(items)
}
fn capture_data(
reader: &mut (impl ReadsDecodable + ?Sized),
buf: &mut Vec<u8>,
) -> Result<(), CodecError> {
let mut guard = codec::DecodingScope::enter(reader)?;
let header: DataHeader = guard.read_data()?;
header.encode(buf)?;
for _ in 0..header.count {
capture_data_with_format(&mut *guard, buf, header.format)?;
}
Ok(())
}
fn capture_data_with_format(
reader: &mut (impl ReadsDecodable + ?Sized),
buf: &mut Vec<u8>,
format: DataFormat,
) -> Result<(), CodecError> {
if format.blob_size > 0 {
let start = buf.len();
buf.resize(start + format.blob_size as usize, 0);
reader.read_exact(&mut buf[start..])?;
}
for _ in 0..format.data_fields {
capture_data(reader, buf)?;
}
Ok(())
}
fn decode_scalar_or_list<T: Decodable + Default>(
reader: &mut (impl ReadsDecodable + ?Sized),
header: DataHeader,
wrap: fn(T) -> Unspecified,
) -> Result<Unspecified, CodecError> {
validate_scalar_format(header.format)?;
match header.count {
0 => Ok(Unspecified::Default),
1 => {
let mut v = T::default();
v.decode(reader, None)?;
Ok(wrap(v))
}
n => {
let mut items = Vec::with_capacity((n as usize).min(1024));
for _ in 0..n {
let mut v = T::default();
v.decode(reader, None)?;
items.push(wrap(v));
}
Ok(Unspecified::List(items))
}
}
}
impl Decodable for Unspecified {
fn decode(
&mut self,
reader: &mut (impl ReadsDecodable + ?Sized),
header: Option<DataHeader>,
) -> Result<(), CodecError> {
let header = match header {
Some(h) => h,
None => {
*self = Unspecified::Default;
return Ok(());
}
};
match Type::from_ordinal(header.format.ordinal) {
Some(Type::Unspecified) => {
for _ in 0..header.count {
reader.skip_blob(header.format.blob_size as usize)?;
for _ in 0..header.format.data_fields {
reader.skip_data()?;
}
}
*self = Unspecified::Default;
}
Some(Type::U8) => *self = decode_scalar_or_list(reader, header, Unspecified::U8)?,
Some(Type::U16) => *self = decode_scalar_or_list(reader, header, Unspecified::U16)?,
Some(Type::U32) => *self = decode_scalar_or_list(reader, header, Unspecified::U32)?,
Some(Type::U64) => *self = decode_scalar_or_list(reader, header, Unspecified::U64)?,
Some(Type::I8) => *self = decode_scalar_or_list(reader, header, Unspecified::I8)?,
Some(Type::I16) => *self = decode_scalar_or_list(reader, header, Unspecified::I16)?,
Some(Type::I32) => *self = decode_scalar_or_list(reader, header, Unspecified::I32)?,
Some(Type::I64) => *self = decode_scalar_or_list(reader, header, Unspecified::I64)?,
Some(Type::F32) => *self = decode_scalar_or_list(reader, header, Unspecified::F32)?,
Some(Type::F64) => *self = decode_scalar_or_list(reader, header, Unspecified::F64)?,
Some(Type::Bool) => *self = decode_scalar_or_list(reader, header, Unspecified::Bool)?,
Some(Type::Text) => {
let header = DataHeader {
count: header.count,
format: DataFormat {
ordinal: 0,
..header.format
},
};
let mut v = Text::default();
v.decode(reader, Some(header))?;
*self = Unspecified::Text(v);
}
Some(Type::List(_)) => {
if header.count != 1
|| header.format.blob_size != 0
|| header.format.data_fields != 1
{
return UnexpectedDataFormatSnafu {
expected: Format::data(243).with(Format::Fluid),
actual: Some(header),
}
.fail();
}
let items = decode_unspecified_list(reader)?;
*self = Unspecified::List(items);
}
Some(Type::Map(_)) => {
if header.count != 1
|| header.format.blob_size != 0
|| header.format.data_fields != 2
{
return UnexpectedDataFormatSnafu {
expected: Format::data(242).with(Format::Fluid).with(Format::Fluid),
actual: Some(header),
}
.fail();
}
let keys_vec = decode_unspecified_list(reader)?;
let values_vec = decode_unspecified_list(reader)?;
if keys_vec.len() != values_vec.len() {
return Err(CodecError::UnspecifiedMapLengthMismatch {
keys: keys_vec.len(),
values: values_vec.len(),
});
}
let mut map = BTreeMap::new();
for (key, value) in keys_vec.into_iter().zip(values_vec) {
match key {
Unspecified::Text(t) => {
map.insert(t, value);
}
other => {
return Err(CodecError::UnsupportedUnspecifiedMapKey {
ordinal: other.type_ordinal(),
})
}
}
}
*self = Unspecified::Map(map);
}
_ => {
let mut raw = Vec::new();
for _ in 0..header.count {
if header.format.blob_size > 0 {
let start = raw.len();
raw.resize(start + header.format.blob_size as usize, 0);
reader.read_exact(&mut raw[start..])?;
}
for _ in 0..header.format.data_fields {
capture_data(reader, &mut raw)?;
}
}
*self = Unspecified::Data { header, raw };
}
}
Ok(())
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Unspecified {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Unspecified::Default => serializer.serialize_unit(),
Unspecified::U8(v) => v.serialize(serializer),
Unspecified::I8(v) => v.serialize(serializer),
Unspecified::U16(v) => v.serialize(serializer),
Unspecified::I16(v) => v.serialize(serializer),
Unspecified::U32(v) => v.serialize(serializer),
Unspecified::I32(v) => v.serialize(serializer),
Unspecified::U64(v) => v.serialize(serializer),
Unspecified::I64(v) => v.serialize(serializer),
Unspecified::F32(v) => v.serialize(serializer),
Unspecified::F64(v) => v.serialize(serializer),
Unspecified::Bool(v) => v.serialize(serializer),
Unspecified::Text(v) => v.serialize(serializer),
Unspecified::List(items) => {
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(items.len()))?;
for elem in items {
seq.serialize_element(elem)?;
}
seq.end()
}
Unspecified::Map(map) => {
use serde::ser::SerializeMap;
let mut m = serializer.serialize_map(Some(map.len()))?;
for (key, value) in map {
m.serialize_entry(key, value)?;
}
m.end()
}
Unspecified::Data { .. } => {
serializer.serialize_unit()
}
}
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Unspecified {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(UnspecifiedVisitor)
}
}
#[cfg(feature = "serde")]
struct UnspecifiedVisitor;
#[cfg(feature = "serde")]
impl<'de> serde::de::Visitor<'de> for UnspecifiedVisitor {
type Value = Unspecified;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("any value")
}
fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
Ok(Unspecified::Default)
}
fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
Ok(Unspecified::Default)
}
fn visit_some<D: serde::Deserializer<'de>>(
self,
deserializer: D,
) -> Result<Self::Value, D::Error> {
serde::Deserialize::deserialize(deserializer)
}
fn visit_bool<E: serde::de::Error>(self, v: bool) -> Result<Self::Value, E> {
Ok(Unspecified::Bool(v))
}
fn visit_u8<E: serde::de::Error>(self, v: u8) -> Result<Self::Value, E> {
Ok(Unspecified::U8(v))
}
fn visit_u16<E: serde::de::Error>(self, v: u16) -> Result<Self::Value, E> {
Ok(Unspecified::U16(v))
}
fn visit_u32<E: serde::de::Error>(self, v: u32) -> Result<Self::Value, E> {
Ok(Unspecified::U32(v))
}
fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<Self::Value, E> {
if let Ok(i) = i64::try_from(v) {
Ok(Unspecified::I64(i))
} else {
Ok(Unspecified::U64(v))
}
}
fn visit_i8<E: serde::de::Error>(self, v: i8) -> Result<Self::Value, E> {
Ok(Unspecified::I8(v))
}
fn visit_i16<E: serde::de::Error>(self, v: i16) -> Result<Self::Value, E> {
Ok(Unspecified::I16(v))
}
fn visit_i32<E: serde::de::Error>(self, v: i32) -> Result<Self::Value, E> {
Ok(Unspecified::I32(v))
}
fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<Self::Value, E> {
Ok(Unspecified::I64(v))
}
fn visit_f32<E: serde::de::Error>(self, v: f32) -> Result<Self::Value, E> {
Ok(Unspecified::F32(v))
}
fn visit_f64<E: serde::de::Error>(self, v: f64) -> Result<Self::Value, E> {
Ok(Unspecified::F64(v))
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
Ok(Unspecified::Text(v.into()))
}
fn visit_string<E: serde::de::Error>(self, v: alloc::string::String) -> Result<Self::Value, E> {
Ok(Unspecified::Text(v.into()))
}
fn visit_seq<A: serde::de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut items = Vec::new();
while let Some(elem) = seq.next_element::<Unspecified>()? {
items.push(elem);
}
Ok(Unspecified::List(items))
}
fn visit_map<A: serde::de::MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
let mut result = BTreeMap::new();
while let Some((key, value)) = map.next_entry::<Text, Unspecified>()? {
result.insert(key, value);
}
Ok(Unspecified::Map(result))
}
}
#[cfg(test)]
mod tests {
use crate::codec::ReadsDecodable;
use super::*;
#[test]
pub fn scalar_round_trips() -> Result<(), CodecError> {
let cases: Vec<Unspecified> = alloc::vec![
Unspecified::U8(42),
Unspecified::I8(-7),
Unspecified::U16(1000),
Unspecified::I16(-500),
Unspecified::U32(100_000),
Unspecified::I32(-50_000),
Unspecified::U64(1_000_000),
Unspecified::I64(-999_999),
Unspecified::F32(3.14),
Unspecified::F64(2.718281828),
Unspecified::Bool(true),
Unspecified::Bool(false),
Unspecified::Text("hello world".into()),
Unspecified::Text("".into()),
];
for original in &cases {
let mut bytes = alloc::vec![];
bytes.write_data(original)?;
let mut decoded = Unspecified::Default;
let header: DataHeader = (&mut bytes.as_slice()).read_data()?;
decoded.decode(&mut bytes.as_slice().split_at(8).1, Some(header))?;
let mut decoded2 = Unspecified::Default;
(&mut bytes.as_slice()).read_data_into(&mut decoded2)?;
assert_eq!(*original, decoded2, "round-trip failed for {original:?}");
}
Ok(())
}
#[test]
pub fn heterogeneous_list_round_trips() -> Result<(), CodecError> {
let original = Unspecified::List(alloc::vec![
Unspecified::I32(1),
Unspecified::Text("two".into()),
Unspecified::Bool(true),
]);
let mut bytes = alloc::vec![];
bytes.write_data(&original)?;
let mut decoded = Unspecified::Default;
(&mut bytes.as_slice()).read_data_into(&mut decoded)?;
assert_eq!(original, decoded);
Ok(())
}
#[test]
pub fn homogeneous_list_round_trips() -> Result<(), CodecError> {
let original = Unspecified::List(alloc::vec![
Unspecified::U32(10),
Unspecified::U32(20),
Unspecified::U32(30),
]);
let mut bytes = alloc::vec![];
bytes.write_data(&original)?;
assert_eq!(
28,
bytes.len(),
"homogeneous U32 list should be compactly encoded"
);
let mut decoded = Unspecified::Default;
(&mut bytes.as_slice()).read_data_into(&mut decoded)?;
assert_eq!(original, decoded);
let original = Unspecified::List(alloc::vec![
Unspecified::Text("hello".into()),
Unspecified::Text("world".into()),
]);
let mut bytes = alloc::vec![];
bytes.write_data(&original)?;
let mut decoded = Unspecified::Default;
(&mut bytes.as_slice()).read_data_into(&mut decoded)?;
assert_eq!(original, decoded);
Ok(())
}
#[test]
pub fn map_round_trips() -> Result<(), CodecError> {
let mut map = BTreeMap::new();
map.insert(Text::from("a"), Unspecified::I32(1));
map.insert(Text::from("b"), Unspecified::Bool(true));
let original = Unspecified::Map(map);
let mut bytes = alloc::vec![];
bytes.write_data(&original)?;
let mut decoded = Unspecified::Default;
(&mut bytes.as_slice()).read_data_into(&mut decoded)?;
assert_eq!(original, decoded);
Ok(())
}
#[test]
pub fn typed_round_trips() -> Result<(), CodecError> {
use super::super::tests::{NestedTestData, TestData};
let test_data = TestData {
number: 1,
floaty: 60.90,
text_list: alloc::vec!["one".into(), "two".into()],
text: "hello".into(),
nested: NestedTestData { boolean: true },
two_d: alloc::vec![
alloc::vec!["three".into(), "four".into()],
alloc::vec!["five".into(), "six".into()],
],
};
let mut static_bytes = alloc::vec![];
static_bytes.write_data(&test_data)?;
let mut decoded = Unspecified::Default;
(&mut static_bytes.as_slice()).read_data_into(&mut decoded)?;
assert!(matches!(decoded, Unspecified::Data { .. }));
let mut re_encoded = alloc::vec![];
re_encoded.write_data(&decoded)?;
assert_eq!(
static_bytes, re_encoded,
"typed round-trip bytes must match"
);
let roundtripped: TestData = re_encoded.as_slice().read_data()?;
assert_eq!(test_data, roundtripped);
Ok(())
}
#[test]
pub fn list_with_default_rejects() {
let with_defaults = Unspecified::List(alloc::vec![
Unspecified::Default,
Unspecified::I32(42),
Unspecified::Default,
]);
let mut bytes = alloc::vec![];
let result = bytes.write_data(&with_defaults);
assert!(
result.is_err(),
"lists containing Default should be rejected"
);
}
#[test]
pub fn scalar_count_gt_one_decodes_as_list() -> Result<(), CodecError> {
let mut bytes = alloc::vec![];
DataHeader {
count: 3,
format: DataFormat {
blob_size: 4,
data_fields: 0,
ordinal: Type::U32.ordinal(),
},
}
.encode(&mut bytes)?;
bytes.extend_from_slice(&10u32.to_le_bytes());
bytes.extend_from_slice(&20u32.to_le_bytes());
bytes.extend_from_slice(&30u32.to_le_bytes());
bytes.write_data(&Unspecified::Bool(true))?;
let mut reader = bytes.as_slice();
let mut first = Unspecified::Default;
(&mut reader).read_data_into(&mut first)?;
assert_eq!(
Unspecified::List(alloc::vec![
Unspecified::U32(10),
Unspecified::U32(20),
Unspecified::U32(30),
]),
first
);
let mut second = Unspecified::Default;
(&mut reader).read_data_into(&mut second)?;
assert_eq!(Unspecified::Bool(true), second);
Ok(())
}
#[test]
pub fn default_encodes_as_zero_header() -> Result<(), CodecError> {
let value = Unspecified::Default;
let mut bytes = alloc::vec![];
bytes.write_data(&value)?;
assert_eq!(8, bytes.len(), "Default should encode as one 8-byte header");
assert!(
bytes.iter().all(|&b| b == 0),
"Default header should be all zeros"
);
let mut decoded = Unspecified::U8(0xFF);
(&mut bytes.as_slice()).read_data_into(&mut decoded)?;
assert_eq!(Unspecified::Default, decoded);
Ok(())
}
}