Something went wrong. Try again.
fork of hydrant
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760use data_encoding::BASE32_NOPAD;use fjall::UserKey;use jacquard_common::types::crypto::{PublicKey, encode_uvarint};use jacquard_common::types::string::Did;use jacquard_common::types::tid::Tid;use jacquard_common::{CowStr, IntoStatic};use miette::{Context, IntoDiagnostic};use serde::{Deserialize, Deserializer, Serialize, Serializer};use smol_str::{SmolStr, SmolStrBuilder, format_smolstr};use std::borrow::Cow;use std::fmt::Display;
const S32_CHAR: &str = "234567abcdefghijklmnopqrstuvwxyz";
#[derive(Clone, Debug, PartialEq, Eq)]pub enum TrimmedDid<'s> { Plc([u8; 15]), Web(CowStr<'s>), Other(CowStr<'s>),}
pub(crate) const TAG_PLC: u8 = 1;pub(crate) const TAG_WEB: u8 = 2;
impl<'s> TrimmedDid<'s> { pub fn len(&self) -> usize { match self { TrimmedDid::Plc(_) => 16, TrimmedDid::Web(s) => 1 + s.len(), TrimmedDid::Other(s) => s.len(), } }
pub fn into_static(self) -> TrimmedDid<'static> { match self { TrimmedDid::Plc(bytes) => TrimmedDid::Plc(bytes), TrimmedDid::Web(s) => TrimmedDid::Web(s.into_static()), TrimmedDid::Other(s) => TrimmedDid::Other(s.into_static()), } } fn from_trimmed_str(value: &'s str) -> Self { let value = value.strip_prefix("did:").unwrap_or(value); if let Some(rest) = value.strip_prefix("plc:") && rest.len() == 24 && let Ok(bytes) = BASE32_NOPAD.decode(rest.to_ascii_uppercase().as_bytes()) && let Ok(bytes) = bytes.try_into() { return TrimmedDid::Plc(bytes); } if let Some(rest) = value.strip_prefix("web:") { return TrimmedDid::Web(CowStr::Borrowed(rest)); } TrimmedDid::Other(CowStr::Borrowed(value)) }
pub fn to_did(&self) -> Did<'static> { match self { TrimmedDid::Plc(_) => { let s = self.to_string(); Did::new_owned(format_smolstr!("did:{}", s)).expect("valid did from plc") } TrimmedDid::Web(s) => { Did::new_owned(format_smolstr!("did:web:{}", s)).expect("valid did from web") } TrimmedDid::Other(s) => { Did::new_owned(format_smolstr!("did:{}", s)).expect("valid did from other") } } }
pub fn write_to_vec(&self, buf: &mut Vec<u8>) { match self { TrimmedDid::Plc(bytes) => { buf.push(TAG_PLC); buf.extend_from_slice(bytes); } TrimmedDid::Web(s) => { buf.push(TAG_WEB); buf.extend_from_slice(s.as_bytes()); } TrimmedDid::Other(s) => buf.extend_from_slice(s.as_bytes()), } }}
impl Display for TrimmedDid<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { TrimmedDid::Plc(bytes) => { f.write_str("plc:")?; f.write_str(&BASE32_NOPAD.encode(bytes).to_ascii_lowercase()) } TrimmedDid::Web(s) => { f.write_str("web:")?; f.write_str(s) } TrimmedDid::Other(s) => f.write_str(s), } }}
impl<'a> From<&'a Did<'a>> for TrimmedDid<'a> { fn from(did: &'a Did<'a>) -> Self { Self::from_trimmed_str(did.as_str()) }}
impl<'a> TryFrom<&'a [u8]> for TrimmedDid<'a> { type Error = miette::Report;
fn try_from(value: &'a [u8]) -> miette::Result<Self> { if value.is_empty() { miette::bail!("empty did key"); } match value[0] { TAG_PLC => { if value.len() == 16 { let mut arr = [0u8; 15]; arr.copy_from_slice(&value[1..]); return Ok(TrimmedDid::Plc(arr)); } miette::bail!("invalid length for tagged plc did"); } TAG_WEB => { if let Ok(s) = std::str::from_utf8(&value[1..]) { return Ok(TrimmedDid::Web(CowStr::Borrowed(s))); } miette::bail!("invalid utf8 for tagged web did"); } _ => { if let Ok(s) = std::str::from_utf8(value) { return Ok(TrimmedDid::Other(CowStr::Borrowed(s))); } miette::bail!("invalid utf8 for other did"); } } }}
impl<'a> From<TrimmedDid<'a>> for UserKey { fn from(val: TrimmedDid<'a>) -> Self { let mut vec = Vec::with_capacity(32); val.write_to_vec(&mut vec); UserKey::new(&vec) }}
impl<'a> From<&TrimmedDid<'a>> for UserKey { fn from(val: &TrimmedDid<'a>) -> Self { let mut vec = Vec::with_capacity(32); val.write_to_vec(&mut vec); UserKey::new(&vec) }}
impl Serialize for TrimmedDid<'_> { fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { let mut vec = Vec::with_capacity(32); self.write_to_vec(&mut vec); serializer.serialize_bytes(&vec) }}
impl<'de> Deserialize<'de> for TrimmedDid<'de> { fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { struct DidVisitor;
fn static_from_bytes<E>(value: &[u8]) -> Result<TrimmedDid<'static>, E> where E: serde::de::Error, { TrimmedDid::try_from(value) .map(TrimmedDid::into_static) .map_err(E::custom) }
impl<'de> serde::de::Visitor<'de> for DidVisitor { type Value = TrimmedDid<'de>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("bytes (tagged) or string (legacy)") }
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: serde::de::Error, { Ok(TrimmedDid::from_trimmed_str(value).into_static()) }
fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E> where E: serde::de::Error, { Ok(TrimmedDid::from_trimmed_str(value)) }
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: serde::de::Error, { Ok(TrimmedDid::from_trimmed_str(&value).into_static()) }
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E> where E: serde::de::Error, { static_from_bytes(value) }
fn visit_borrowed_bytes<E>(self, value: &'de [u8]) -> Result<Self::Value, E> where E: serde::de::Error, { TrimmedDid::try_from(value).map_err(E::custom) }
fn visit_byte_buf<E>(self, value: Vec<u8>) -> Result<Self::Value, E> where E: serde::de::Error, { static_from_bytes(&value) }
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: serde::de::SeqAccess<'de>, { let mut bytes = Vec::with_capacity(seq.size_hint().unwrap_or(0).min(2048)); while let Some(byte) = seq.next_element()? { bytes.push(byte); } static_from_bytes(&bytes) } }
deserializer.deserialize_any(DidVisitor) }}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]pub struct DbTid(#[serde(with = "serde_bytes")] [u8; 8]);
impl DbTid { pub fn new_from_bytes(bytes: [u8; 8]) -> Self { Self(bytes) }
pub fn len(&self) -> usize { self.0.len() }
pub fn as_bytes(&self) -> &[u8] { &self.0 }
pub fn to_tid(self) -> Tid { Tid::raw(self.to_smolstr()) }
pub fn add_micros(self, micros: u64) -> Self { let raw = u64::from_be_bytes(self.0); let next_micros = (raw >> 10).saturating_add(micros); Self((next_micros << 10).to_be_bytes()) }
fn to_smolstr(self) -> SmolStr { let mut i = u64::from_be_bytes(self.0); let mut s = SmolStrBuilder::new(); for _ in 0..13 { let c = i & 0x1F; s.push(S32_CHAR.chars().nth(c as usize).unwrap()); i >>= 5; }
let mut builder = SmolStrBuilder::new(); for c in s.finish().chars().rev() { builder.push(c); } builder.finish() }}
pub fn s32decode(s: &str) -> u64 { let mut i: usize = 0; for c in s.chars() { i = i * 32 + S32_CHAR.chars().position(|x| x == c).unwrap(); } i as u64}
impl From<&Tid> for DbTid { fn from(tid: &Tid) -> Self { DbTid(s32decode(tid.as_str()).to_be_bytes()) }}
impl From<DbTid> for Tid { fn from(val: DbTid) -> Self { val.to_tid() }}
impl Display for DbTid { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.to_smolstr()) }}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]#[repr(u8)]#[cfg_attr(not(feature = "indexer"), allow(dead_code))]pub enum DbAction { Create = 0, Update = 1, Delete = 2,}
#[cfg_attr(not(feature = "indexer"), allow(dead_code))]impl DbAction { pub fn as_str(&self) -> &'static str { match self { DbAction::Create => "create", DbAction::Update => "update", DbAction::Delete => "delete", } }}
impl<'a> TryFrom<&'a str> for DbAction { type Error = miette::Report;
fn try_from(s: &'a str) -> Result<Self, Self::Error> { match s { "create" => Ok(DbAction::Create), "update" => Ok(DbAction::Update), "delete" => Ok(DbAction::Delete), _ => miette::bail!("invalid action: {}", s), } }}
impl Display for DbAction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.as_str()) }}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]#[serde(untagged)]#[cfg_attr(not(feature = "indexer"), allow(dead_code))]pub enum DbRkey { Tid(DbTid), Str(SmolStr),}
impl<'de> Deserialize<'de> for DbRkey { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { struct DbRkeyVisitor;
fn tid_from_bytes<E>(value: &[u8]) -> Result<DbRkey, E> where E: serde::de::Error, { let bytes = value .try_into() .map_err(|_| E::invalid_length(value.len(), &"exactly 8 bytes"))?; Ok(DbRkey::Tid(DbTid::new_from_bytes(bytes))) }
impl<'de> serde::de::Visitor<'de> for DbRkeyVisitor { type Value = DbRkey;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { formatter.write_str("an rkey string or an 8-byte tid") }
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: serde::de::Error, { Ok(DbRkey::Str(SmolStr::from(value))) }
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: serde::de::Error, { Ok(DbRkey::Str(SmolStr::from(value))) }
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E> where E: serde::de::Error, { tid_from_bytes(value) }
fn visit_byte_buf<E>(self, value: Vec<u8>) -> Result<Self::Value, E> where E: serde::de::Error, { tid_from_bytes(&value) }
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> where A: serde::de::SeqAccess<'de>, { let mut bytes = [0; 8]; for (index, byte) in bytes.iter_mut().enumerate() { *byte = seq .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(index, &self))?; } if seq.next_element::<serde::de::IgnoredAny>()?.is_some() { return Err(serde::de::Error::invalid_length(9, &self)); } Ok(DbRkey::Tid(DbTid::new_from_bytes(bytes))) } }
deserializer.deserialize_any(DbRkeyVisitor) }}
#[cfg_attr(not(feature = "indexer"), allow(dead_code))]impl DbRkey { pub fn new(s: &str) -> Self { if let Ok(tid) = Tid::new(s) { DbRkey::Tid(DbTid::from(&tid)) } else { DbRkey::Str(SmolStr::from(s)) } }
pub fn to_smolstr(&self) -> SmolStr { match self { DbRkey::Tid(tid) => tid.to_smolstr(), DbRkey::Str(s) => s.clone(), } }}
impl From<&str> for DbRkey { fn from(s: &str) -> Self { Self::new(s) }}
impl From<String> for DbRkey { fn from(s: String) -> Self { Self::new(&s) }}
impl From<SmolStr> for DbRkey { fn from(s: SmolStr) -> Self { Self::new(s.as_str()) }}
impl Display for DbRkey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.to_smolstr()) }}
/// did:key:z... → raw multicodec public key bytes#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, jacquard_derive::IntoStatic)]pub struct DidKey<'b>( #[serde(borrow)] #[serde(with = "serde_bytes")] pub Cow<'b, [u8]>,);
impl DidKey<'_> { #[allow(dead_code)] pub fn from_did_key(s: &str) -> miette::Result<Self> { let multibase_str = s .strip_prefix("did:key:") .ok_or_else(|| miette::miette!("missing did:key: prefix in {s}"))?; let (_base, bytes) = multibase::decode(multibase_str) .into_diagnostic() .wrap_err("invalid multibase in did:key")?; Ok(Self(Cow::Owned(bytes))) }
pub fn encode(&self) -> String { multibase::encode(multibase::Base::Base58Btc, &self.0) }}
impl std::fmt::Display for DidKey<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "did:key:{}", self.encode()) }}
impl From<PublicKey<'_>> for DidKey<'static> { fn from(value: PublicKey<'_>) -> Self { use jacquard_common::types::crypto::KeyCodec; fn code_of(codec: KeyCodec) -> u64 { match codec { KeyCodec::Ed25519 => 0xED, KeyCodec::Secp256k1 => 0xE7, KeyCodec::P256 => 0x1200, KeyCodec::Unknown(c) => c, } } let mut bytes = Vec::with_capacity(8 + value.bytes.len()); bytes.append(&mut encode_uvarint(code_of(value.codec))); bytes.extend_from_slice(&value.bytes); Self(bytes.into()) }}
#[cfg(test)]mod tests { use super::*; use jacquard_common::types::tid::Tid;
#[test] fn test_dbtid_roundtrip() { let tid_str = "3jzfcijpj2z2a"; let tid = Tid::new(tid_str).unwrap(); let db_tid = DbTid::from(&tid); assert_eq!(db_tid.to_tid().as_str(), tid_str);
let tid_str_2 = "2222222222222"; let tid = Tid::new(tid_str_2).unwrap(); let db_tid = DbTid::from(&tid); assert_eq!(db_tid.to_tid().as_str(), tid_str_2); }
#[test] fn db_representation_conversions_are_lossless() { for did in [ "did:plc:ewvi7nxzyoun6zhxrhs64oiz", "did:web:example.com", "did:example:opaque", ] { let did = Did::new(did).unwrap(); let trimmed = TrimmedDid::from(&did); let mut bytes = Vec::new(); trimmed.write_to_vec(&mut bytes);
assert_eq!(TrimmedDid::try_from(bytes.as_slice()).unwrap(), trimmed); assert_eq!(trimmed.to_did(), did); assert_eq!(trimmed.to_string(), did.as_str().trim_start_matches("did:")); assert_eq!( UserKey::from(trimmed.clone()), UserKey::from(&trimmed), "owned and borrowed key conversions diverged for {did}" ); assert_eq!(trimmed.clone().into_static(), trimmed); }
for malformed in [ &[][..], &[TAG_PLC][..], &[TAG_PLC, 0][..], &[TAG_WEB, 0xff][..], &[0xff][..], ] { assert!(TrimmedDid::try_from(malformed).is_err()); }
let tid = Tid::new("3jzfcijpj2z2a").unwrap(); let db_tid = DbTid::from(&tid); assert_eq!(Tid::from(db_tid), tid); assert_eq!(db_tid.to_string(), tid.as_str());
for value in ["self", "stinkpot", "3jzfcijpj2z2a"] { let from_str = DbRkey::from(value); assert_eq!(DbRkey::from(value.to_owned()), from_str); assert_eq!(DbRkey::from(SmolStr::from(value)), from_str); assert_eq!(from_str.to_string(), value); }
use jacquard_common::types::crypto::KeyCodec; for (codec, prefix) in [ (KeyCodec::Ed25519, &[0xed, 0x01][..]), (KeyCodec::Secp256k1, &[0xe7, 0x01][..]), (KeyCodec::P256, &[0x80, 0x24][..]), (KeyCodec::Unknown(0x42), &[0x42][..]), ] { let key = DidKey::from(PublicKey { codec, bytes: Cow::Borrowed(&[1, 2, 3]), }); assert_eq!(key.0.strip_suffix(&[1, 2, 3]), Some(prefix)); assert_eq!(DidKey::from_did_key(&key.to_string()).unwrap(), key); } }
#[test] fn test_dbrkey() { let tid_str = "3jzfcijpj2z2a"; let rkey = DbRkey::new(tid_str); if let DbRkey::Tid(t) = rkey { assert_eq!(t.to_tid().as_str(), tid_str); } else { panic!("expected tid"); }
let str_val = "self"; let rkey = DbRkey::new(str_val); if let DbRkey::Str(s) = rkey { assert_eq!(s, str_val); } else { panic!("expected str"); } }
#[test] fn dbrkey_msgpack_string_marker_preserves_eight_byte_rkey() { let encoded = rmp_serde::to_vec(&DbRkey::Str("stinkpot".into())).unwrap(); assert_eq!(encoded, b"\xa8stinkpot");
let decoded: DbRkey = rmp_serde::from_slice(&encoded).unwrap(); assert_eq!(decoded, DbRkey::Str("stinkpot".into())); assert_eq!(decoded.to_smolstr(), "stinkpot"); }
#[test] fn dbrkey_msgpack_binary_marker_preserves_tid() { let expected = DbRkey::new("3jzfcijpj2z2a"); let encoded = rmp_serde::to_vec(&expected).unwrap(); assert_eq!(encoded.get(..2), Some([0xc4, 8].as_slice()));
let decoded: DbRkey = rmp_serde::from_slice(&encoded).unwrap(); assert_eq!(decoded, expected); assert_eq!(decoded.to_smolstr(), "3jzfcijpj2z2a"); }
#[test] fn dbrkey_json_tid_sequence_stays_compatible() { let expected = DbRkey::new("3jzfcijpj2z2a"); let encoded = serde_json::to_vec(&expected).unwrap(); assert_eq!(encoded.first(), Some(&b'['));
let decoded: DbRkey = serde_json::from_slice(&encoded).unwrap(); assert_eq!(decoded, expected); } #[test] fn trimmed_did_accepts_legacy_msgpack_strings() { for did in [ "did:plc:ewvi7nxzyoun6zhxrhs64oiz", "did:web:example.com", "did:example:opaque", ] { let parsed = Did::new(did).unwrap(); let expected = TrimmedDid::from(&parsed); for legacy in [did, did.strip_prefix("did:").unwrap()] { let encoded = rmp_serde::to_vec(legacy).unwrap();
let decoded: TrimmedDid<'_> = rmp_serde::from_slice(&encoded).unwrap(); assert_eq!(decoded, expected); assert_eq!(decoded.to_did().as_str(), did); } } }
#[test] fn trimmed_did_binary_and_json_sequence_roundtrip() { for (did, golden) in [ ( "did:plc:ewvi7nxzyoun6zhxrhs64oiz", "c4100125aa8fb6f9c3a8df64f789e5ee3919", ), ("did:web:example.com", "c40c026578616d706c652e636f6d"), ("did:example:opaque", "c40e6578616d706c653a6f7061717565"), ] { let parsed = Did::new(did).unwrap(); let expected = TrimmedDid::from(&parsed);
let msgpack = rmp_serde::to_vec(&expected).unwrap(); assert_eq!(msgpack.first(), Some(&0xc4)); assert_eq!(hex::encode(&msgpack), golden); let from_msgpack: TrimmedDid<'_> = rmp_serde::from_slice(&msgpack).unwrap(); assert_eq!(from_msgpack, expected);
let json = serde_json::to_vec(&expected).unwrap(); assert_eq!(json.first(), Some(&b'[')); let from_json: TrimmedDid<'_> = serde_json::from_slice(&json).unwrap(); assert_eq!(from_json, expected); } }
#[test] fn dbrkey_string_markers_preserve_every_utf8_length() { let mut values = (0..=24) .map(|length| "x".repeat(length)) .collect::<Vec<_>>(); values.extend(["stinkpot".to_owned(), "éééé".to_owned()]);
for value in values { let expected = DbRkey::Str(value.clone().into()); let encoded = rmp_serde::to_vec(&expected).unwrap(); let decoded: DbRkey = rmp_serde::from_slice(&encoded).unwrap(); assert_eq!(decoded, expected, "failed for {value:?}"); } }
#[test] fn dbrkey_binary_marker_requires_exactly_eight_bytes() { for length in [0, 1, 7, 9, 16] { let encoded = rmp_serde::to_vec(&serde_bytes::ByteBuf::from(vec![b'x'; length])).unwrap(); assert!(rmp_serde::from_slice::<DbRkey>(&encoded).is_err()); }
let encoded = rmp_serde::to_vec(&serde_bytes::ByteBuf::from(b"stinkpot".to_vec())).unwrap(); let decoded: DbRkey = rmp_serde::from_slice(&encoded).unwrap(); assert!(matches!(decoded, DbRkey::Tid(_))); }
#[test] fn dbrkey_json_sequence_requires_exactly_eight_bytes() { for length in [0, 1, 7, 9, 16] { let encoded = serde_json::to_vec(&vec![0_u8; length]).unwrap(); assert!(serde_json::from_slice::<DbRkey>(&encoded).is_err()); }
let decoded: DbRkey = serde_json::from_slice(b"[0,1,2,3,4,5,6,7]").unwrap(); assert_eq!( decoded, DbRkey::Tid(DbTid::new_from_bytes([0, 1, 2, 3, 4, 5, 6, 7])) ); }
#[test] fn db_action_and_did_key_representation_contracts() { for (action, golden) in [ (DbAction::Create, "a6437265617465"), (DbAction::Update, "a6557064617465"), (DbAction::Delete, "a644656c657465"), ] { let msgpack = rmp_serde::to_vec(&action).unwrap(); assert_eq!(hex::encode(&msgpack), golden); assert_eq!(rmp_serde::from_slice::<DbAction>(&msgpack).unwrap(), action); assert_eq!(DbAction::try_from(action.as_str()).unwrap(), action); }
let expected = DidKey(Cow::Borrowed(&[0xed, 0x01, 0x02, 0x03])); let msgpack = rmp_serde::to_vec(&expected).unwrap(); assert_eq!(hex::encode(&msgpack), "c404ed010203"); let decoded: DidKey<'_> = rmp_serde::from_slice(&msgpack).unwrap(); assert_eq!(decoded, expected);
let display = expected.to_string(); assert_eq!(DidKey::from_did_key(&display).unwrap(), expected); }}