use crate::error::Result;
use crate::value::Value;
use std::collections::{BTreeMap, HashMap};
pub trait Crous: Sized {
fn to_crous_value(&self) -> Value;
fn from_crous_value(value: &Value) -> Result<Self>;
fn schema_fingerprint() -> u64;
fn type_name() -> &'static str;
fn to_crous_bytes(&self) -> Result<Vec<u8>> {
let val = self.to_crous_value();
let mut enc = crate::encoder::Encoder::new();
enc.encode_value(&val)?;
enc.finish()
}
fn from_crous_bytes(data: &[u8]) -> Result<Self> {
let mut dec = crate::decoder::Decoder::new(data);
let val = dec.decode_next()?.to_owned_value();
Self::from_crous_value(&val)
}
fn from_crous_bytes_with_limits(data: &[u8], limits: crate::limits::Limits) -> Result<Self> {
let mut dec = crate::decoder::Decoder::with_limits(data, limits);
let val = dec.decode_next()?.to_owned_value();
Self::from_crous_value(&val)
}
}
impl Crous for bool {
fn to_crous_value(&self) -> Value {
Value::Bool(*self)
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Bool(b) => Ok(*b),
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected bool".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0004
}
fn type_name() -> &'static str {
"bool"
}
}
macro_rules! impl_crous_uint {
($($ty:ty => $fp:expr, $name:expr);+ $(;)?) => { $(
impl Crous for $ty {
fn to_crous_value(&self) -> Value {
Value::UInt(*self as u64)
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::UInt(n) => {
<$ty>::try_from(*n).map_err(|_| crate::error::CrousError::SchemaMismatch(
format!("uint {} out of range for {}", n, $name)
))
}
_ => Err(crate::error::CrousError::SchemaMismatch(
format!("expected uint for {}", $name)
)),
}
}
fn schema_fingerprint() -> u64 { $fp }
fn type_name() -> &'static str { $name }
}
)+ };
}
impl_crous_uint! {
u8 => 0x0002_0001, "u8";
u16 => 0x0002_0002, "u16";
u32 => 0x0002_0003, "u32";
u64 => 0x0002, "u64";
}
impl Crous for usize {
fn to_crous_value(&self) -> Value {
Value::UInt(*self as u64)
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::UInt(n) => {
usize::try_from(*n).map_err(|_| {
crate::error::CrousError::SchemaMismatch(format!(
"uint {n} out of range for usize"
))
})
}
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected uint for usize".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0002_0005
}
fn type_name() -> &'static str {
"usize"
}
}
impl Crous for u128 {
fn to_crous_value(&self) -> Value {
Value::UInt(u64::try_from(*self).expect("u128 value exceeds u64::MAX for Crous encoding"))
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::UInt(n) => Ok(u128::from(*n)),
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected uint for u128".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0002_0006
}
fn type_name() -> &'static str {
"u128"
}
}
macro_rules! impl_crous_int {
($($ty:ty => $fp:expr, $name:expr);+ $(;)?) => { $(
impl Crous for $ty {
fn to_crous_value(&self) -> Value {
Value::Int(i64::from(*self))
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Int(n) => {
<$ty>::try_from(*n).map_err(|_| crate::error::CrousError::SchemaMismatch(
format!("int {} out of range for {}", n, $name)
))
}
Value::UInt(n) => {
let as_i64 = i64::try_from(*n).map_err(|_| {
crate::error::CrousError::SchemaMismatch(
format!("uint {} out of range for {}", n, $name)
)
})?;
<$ty>::try_from(as_i64).map_err(|_| {
crate::error::CrousError::SchemaMismatch(
format!("uint {} out of range for {}", n, $name)
)
})
}
_ => Err(crate::error::CrousError::SchemaMismatch(
format!("expected int for {}", $name)
)),
}
}
fn schema_fingerprint() -> u64 { $fp }
fn type_name() -> &'static str { $name }
}
)+ };
}
impl_crous_int! {
i8 => 0x0003_0001, "i8";
i16 => 0x0003_0002, "i16";
i32 => 0x0003_0003, "i32";
i64 => 0x0003, "i64";
}
impl Crous for isize {
fn to_crous_value(&self) -> Value {
Value::Int(*self as i64)
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Int(n) => {
isize::try_from(*n).map_err(|_| {
crate::error::CrousError::SchemaMismatch(format!(
"int {n} out of range for isize"
))
})
}
Value::UInt(n) => {
let as_i64 = i64::try_from(*n).map_err(|_| {
crate::error::CrousError::SchemaMismatch(format!(
"uint {n} out of range for isize"
))
})?;
isize::try_from(as_i64).map_err(|_| {
crate::error::CrousError::SchemaMismatch(format!(
"int {as_i64} out of range for isize"
))
})
}
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected int for isize".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0003_0005
}
fn type_name() -> &'static str {
"isize"
}
}
impl Crous for i128 {
fn to_crous_value(&self) -> Value {
Value::Int(i64::try_from(*self).expect("i128 value exceeds i64 range for Crous encoding"))
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Int(n) => Ok(i128::from(*n)),
Value::UInt(n) => Ok(i128::from(*n)),
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected int for i128".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0003_0006
}
fn type_name() -> &'static str {
"i128"
}
}
impl Crous for f64 {
fn to_crous_value(&self) -> Value {
Value::Float(*self)
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Float(f) => Ok(*f),
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected float".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0005
}
fn type_name() -> &'static str {
"f64"
}
}
impl Crous for f32 {
fn to_crous_value(&self) -> Value {
Value::Float(f64::from(*self))
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Float(f) => Ok(*f as f32),
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected float for f32".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0005_0001
}
fn type_name() -> &'static str {
"f32"
}
}
impl Crous for String {
fn to_crous_value(&self) -> Value {
Value::Str(self.clone())
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Str(s) => Ok(s.clone()),
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected string".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0001
}
fn type_name() -> &'static str {
"String"
}
}
impl Crous for Box<str> {
fn to_crous_value(&self) -> Value {
Value::Str(self.to_string())
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Str(s) => Ok(s.clone().into_boxed_str()),
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected string for Box<str>".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0001_0001
}
fn type_name() -> &'static str {
"Box<str>"
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CrousBytes(pub Vec<u8>);
impl CrousBytes {
pub fn new(bytes: Vec<u8>) -> Self {
Self(bytes)
}
pub fn into_inner(self) -> Vec<u8> {
self.0
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl From<Vec<u8>> for CrousBytes {
fn from(v: Vec<u8>) -> Self {
Self(v)
}
}
impl From<CrousBytes> for Vec<u8> {
fn from(b: CrousBytes) -> Self {
b.0
}
}
impl AsRef<[u8]> for CrousBytes {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl Crous for CrousBytes {
fn to_crous_value(&self) -> Value {
Value::Bytes(self.0.clone())
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Bytes(b) => Ok(CrousBytes(b.clone())),
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected bytes".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0006
}
fn type_name() -> &'static str {
"CrousBytes"
}
}
impl<T: Crous> Crous for Vec<T> {
fn to_crous_value(&self) -> Value {
Value::Array(self.iter().map(|item| item.to_crous_value()).collect())
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Array(items) => items.iter().map(T::from_crous_value).collect(),
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected array".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
T::schema_fingerprint()
.wrapping_mul(31)
.wrapping_add(0x0010)
}
fn type_name() -> &'static str {
"Vec"
}
}
impl<T: Crous> Crous for Option<T> {
fn to_crous_value(&self) -> Value {
match self {
Some(v) => v.to_crous_value(),
None => Value::Null,
}
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Null => Ok(None),
other => Ok(Some(T::from_crous_value(other)?)),
}
}
fn schema_fingerprint() -> u64 {
T::schema_fingerprint()
.wrapping_mul(37)
.wrapping_add(0x0020)
}
fn type_name() -> &'static str {
"Option"
}
}
impl<T: Crous> Crous for Box<T> {
fn to_crous_value(&self) -> Value {
(**self).to_crous_value()
}
fn from_crous_value(value: &Value) -> Result<Self> {
T::from_crous_value(value).map(Box::new)
}
fn schema_fingerprint() -> u64 {
T::schema_fingerprint()
}
fn type_name() -> &'static str {
T::type_name()
}
}
impl<T: Crous> Crous for HashMap<String, T> {
fn to_crous_value(&self) -> Value {
Value::Object(
self.iter()
.map(|(k, v)| (k.clone(), v.to_crous_value()))
.collect(),
)
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Object(entries) => {
let mut map = HashMap::with_capacity(entries.len());
for (k, v) in entries {
map.insert(k.clone(), T::from_crous_value(v)?);
}
Ok(map)
}
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected object for HashMap".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
T::schema_fingerprint()
.wrapping_mul(41)
.wrapping_add(0x0030)
}
fn type_name() -> &'static str {
"HashMap"
}
}
impl<T: Crous> Crous for BTreeMap<String, T> {
fn to_crous_value(&self) -> Value {
Value::Object(
self.iter()
.map(|(k, v)| (k.clone(), v.to_crous_value()))
.collect(),
)
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Object(entries) => {
let mut map = BTreeMap::new();
for (k, v) in entries {
map.insert(k.clone(), T::from_crous_value(v)?);
}
Ok(map)
}
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected object for BTreeMap".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
T::schema_fingerprint()
.wrapping_mul(43)
.wrapping_add(0x0031)
}
fn type_name() -> &'static str {
"BTreeMap"
}
}
macro_rules! impl_crous_tuple {
($fp:expr, $($idx:tt : $T:ident),+) => {
impl<$($T: Crous),+> Crous for ($($T,)+) {
fn to_crous_value(&self) -> Value {
Value::Array(vec![
$(self.$idx.to_crous_value()),+
])
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Array(items) => {
let mut iter = items.iter();
Ok(($(
$T::from_crous_value(
iter.next().ok_or_else(|| {
crate::error::CrousError::SchemaMismatch(
"tuple: not enough array elements".into()
)
})?
)?,
)+))
}
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected array for tuple".into(),
)),
}
}
fn schema_fingerprint() -> u64 { $fp }
fn type_name() -> &'static str { "tuple" }
}
};
}
impl_crous_tuple!(0x0040_0001, 0: A);
impl_crous_tuple!(0x0040_0002, 0: A, 1: B);
impl_crous_tuple!(0x0040_0003, 0: A, 1: B, 2: C);
impl_crous_tuple!(0x0040_0004, 0: A, 1: B, 2: C, 3: D);
impl_crous_tuple!(0x0040_0005, 0: A, 1: B, 2: C, 3: D, 4: E);
impl_crous_tuple!(0x0040_0006, 0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
impl Crous for () {
fn to_crous_value(&self) -> Value {
Value::Null
}
fn from_crous_value(value: &Value) -> Result<Self> {
match value {
Value::Null => Ok(()),
_ => Err(crate::error::CrousError::SchemaMismatch(
"expected null for ()".into(),
)),
}
}
fn schema_fingerprint() -> u64 {
0x0000
}
fn type_name() -> &'static str {
"()"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn u8_roundtrip() {
let v: u8 = 200;
let val = v.to_crous_value();
assert_eq!(val, Value::UInt(200));
assert_eq!(u8::from_crous_value(&val).unwrap(), 200);
}
#[test]
fn u8_overflow() {
let val = Value::UInt(256);
assert!(u8::from_crous_value(&val).is_err());
}
#[test]
fn u16_roundtrip() {
let v: u16 = 60000;
let val = v.to_crous_value();
assert_eq!(u16::from_crous_value(&val).unwrap(), 60000);
}
#[test]
fn u32_roundtrip() {
let v: u32 = u32::MAX;
let val = v.to_crous_value();
assert_eq!(u32::from_crous_value(&val).unwrap(), u32::MAX);
}
#[test]
fn i8_roundtrip() {
let v: i8 = -42;
let val = v.to_crous_value();
assert_eq!(val, Value::Int(-42));
assert_eq!(i8::from_crous_value(&val).unwrap(), -42);
}
#[test]
fn i8_overflow() {
let val = Value::Int(200);
assert!(i8::from_crous_value(&val).is_err());
}
#[test]
fn i16_roundtrip() {
let v: i16 = -30000;
let val = v.to_crous_value();
assert_eq!(i16::from_crous_value(&val).unwrap(), -30000);
}
#[test]
fn i32_roundtrip() {
let v: i32 = i32::MIN;
let val = v.to_crous_value();
assert_eq!(i32::from_crous_value(&val).unwrap(), i32::MIN);
}
#[test]
fn i32_from_uint_compat() {
let val = Value::UInt(42);
assert_eq!(i32::from_crous_value(&val).unwrap(), 42);
}
#[test]
fn f32_roundtrip() {
let v: f32 = 2.78;
let val = v.to_crous_value();
assert!(matches!(val, Value::Float(_)));
let back = f32::from_crous_value(&val).unwrap();
assert!((back - 2.78).abs() < 1e-5);
}
#[test]
fn vec_u8_is_array() {
let v: Vec<u8> = vec![1, 2, 3];
let val = v.to_crous_value();
assert!(matches!(val, Value::Array(_)));
let back = Vec::<u8>::from_crous_value(&val).unwrap();
assert_eq!(back, vec![1, 2, 3]);
}
#[test]
fn crous_bytes_is_bytes() {
let blob = CrousBytes(vec![0xDE, 0xAD, 0xBE, 0xEF]);
let val = blob.to_crous_value();
assert!(matches!(val, Value::Bytes(_)));
let back = CrousBytes::from_crous_value(&val).unwrap();
assert_eq!(back.0, vec![0xDE, 0xAD, 0xBE, 0xEF]);
}
#[test]
fn box_str_roundtrip() {
let s: Box<str> = "hello".into();
let val = s.to_crous_value();
assert_eq!(val, Value::Str("hello".into()));
let back = Box::<str>::from_crous_value(&val).unwrap();
assert_eq!(&*back, "hello");
}
#[test]
fn box_t_roundtrip() {
let v: Box<u32> = Box::new(42);
let val = v.to_crous_value();
assert_eq!(val, Value::UInt(42));
let back = Box::<u32>::from_crous_value(&val).unwrap();
assert_eq!(*back, 42);
}
#[test]
fn hashmap_roundtrip() {
let mut map = HashMap::new();
map.insert("x".to_string(), 10u64);
map.insert("y".to_string(), 20u64);
let val = map.to_crous_value();
assert!(matches!(val, Value::Object(_)));
let back = HashMap::<String, u64>::from_crous_value(&val).unwrap();
assert_eq!(back.get("x"), Some(&10));
assert_eq!(back.get("y"), Some(&20));
}
#[test]
fn btreemap_roundtrip() {
let mut map = BTreeMap::new();
map.insert("a".to_string(), Value::Bool(true));
map.insert("b".to_string(), Value::UInt(7));
let mut m2 = BTreeMap::new();
m2.insert("flag".to_string(), true);
m2.insert("enabled".to_string(), false);
let val = m2.to_crous_value();
let back = BTreeMap::<String, bool>::from_crous_value(&val).unwrap();
assert_eq!(back.get("flag"), Some(&true));
assert_eq!(back.get("enabled"), Some(&false));
}
#[test]
fn tuple2_roundtrip() {
let t = (42u32, "hello".to_string());
let val = t.to_crous_value();
assert!(matches!(val, Value::Array(_)));
let back = <(u32, String)>::from_crous_value(&val).unwrap();
assert_eq!(back, (42, "hello".to_string()));
}
#[test]
fn tuple3_roundtrip() {
let t = (true, -5i16, 2.78f64);
let val = t.to_crous_value();
let back = <(bool, i16, f64)>::from_crous_value(&val).unwrap();
assert!(back.0);
assert_eq!(back.1, -5);
assert!((back.2 - 2.78).abs() < 1e-10);
}
#[test]
fn unit_roundtrip() {
let val = ().to_crous_value();
assert_eq!(val, Value::Null);
assert_eq!(<()>::from_crous_value(&val).unwrap(), ());
}
#[test]
fn usize_roundtrip() {
let v: usize = 999;
let val = v.to_crous_value();
assert_eq!(usize::from_crous_value(&val).unwrap(), 999);
}
#[test]
fn isize_roundtrip() {
let v: isize = -999;
let val = v.to_crous_value();
assert_eq!(isize::from_crous_value(&val).unwrap(), -999);
}
#[test]
fn u128_small_roundtrip() {
let v: u128 = 123456;
let val = v.to_crous_value();
assert_eq!(u128::from_crous_value(&val).unwrap(), 123456);
}
#[test]
fn i128_small_roundtrip() {
let v: i128 = -123456;
let val = v.to_crous_value();
assert_eq!(i128::from_crous_value(&val).unwrap(), -123456);
}
#[test]
fn nested_option_vec() {
let v: Option<Vec<u8>> = Some(vec![10, 20, 30]);
let val = v.to_crous_value();
let back = Option::<Vec<u8>>::from_crous_value(&val).unwrap();
assert_eq!(back, Some(vec![10, 20, 30]));
}
#[test]
fn option_none() {
let v: Option<u8> = None;
let val = v.to_crous_value();
assert_eq!(val, Value::Null);
let back = Option::<u8>::from_crous_value(&val).unwrap();
assert_eq!(back, None);
}
#[test]
fn full_binary_roundtrip_u8() {
let v: u8 = 42;
let bytes = v.to_crous_bytes().unwrap();
let back = u8::from_crous_bytes(&bytes).unwrap();
assert_eq!(back, 42);
}
#[test]
fn full_binary_roundtrip_crous_bytes() {
let blob = CrousBytes(vec![1, 2, 3, 4]);
let bytes = blob.to_crous_bytes().unwrap();
let back = CrousBytes::from_crous_bytes(&bytes).unwrap();
assert_eq!(back.0, vec![1, 2, 3, 4]);
}
#[test]
fn full_binary_roundtrip_tuple() {
let t = (100u16, "world".to_string(), false);
let bytes = t.to_crous_bytes().unwrap();
let back = <(u16, String, bool)>::from_crous_bytes(&bytes).unwrap();
assert_eq!(back, (100, "world".to_string(), false));
}
#[test]
fn full_binary_roundtrip_hashmap() {
let mut m = HashMap::new();
m.insert("key".to_string(), 99u32);
let bytes = m.to_crous_bytes().unwrap();
let back = HashMap::<String, u32>::from_crous_bytes(&bytes).unwrap();
assert_eq!(back.get("key"), Some(&99));
}
}