use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::address::ContentAddress;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CodecError {
Encode(String),
Decode(String),
}
impl core::fmt::Display for CodecError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CodecError::Encode(e) => write!(f, "DAG-CBOR encode: {e}"),
CodecError::Decode(e) => write!(f, "DAG-CBOR decode: {e}"),
}
}
}
impl std::error::Error for CodecError {}
pub fn canonical_encode<T: Serialize>(value: &T) -> Result<Vec<u8>, CodecError> {
serde_ipld_dagcbor::to_vec(value).map_err(|e| CodecError::Encode(e.to_string()))
}
pub fn canonical_decode<T: DeserializeOwned>(bytes: &[u8]) -> Result<T, CodecError> {
serde_ipld_dagcbor::from_slice(bytes).map_err(|e| CodecError::Decode(e.to_string()))
}
pub fn address_of<T: Serialize>(value: &T) -> Result<ContentAddress, CodecError> {
Ok(ContentAddress::of(&canonical_encode(value)?))
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
#[test]
fn encoding_is_deterministic() {
let mut m = BTreeMap::new();
m.insert("a".to_string(), 1u32);
m.insert("b".to_string(), 2u32);
assert_eq!(canonical_encode(&m).unwrap(), canonical_encode(&m).unwrap());
}
#[test]
fn equal_values_share_an_address() {
let v = ("functor", vec!["A", "B"], 3u8);
assert_eq!(address_of(&v).unwrap(), address_of(&v).unwrap());
}
#[test]
fn distinct_values_get_distinct_addresses() {
assert_ne!(address_of(&"A").unwrap(), address_of(&"B").unwrap());
}
#[test]
fn structurally_equal_maps_address_equal_regardless_of_build_order() {
let mut m1 = BTreeMap::new();
m1.insert("z", 1u32);
m1.insert("a", 2u32);
let mut m2 = BTreeMap::new();
m2.insert("a", 2u32);
m2.insert("z", 1u32);
assert_eq!(address_of(&m1).unwrap(), address_of(&m2).unwrap());
}
#[test]
fn decode_inverts_encode() {
let v: Vec<(String, u32)> = vec![("a".into(), 1), ("b".into(), 2)];
let bytes = canonical_encode(&v).unwrap();
let back: Vec<(String, u32)> = canonical_decode(&bytes).unwrap();
assert_eq!(v, back);
}
}