use core::fmt;
use core::str::FromStr;
use serde::{Deserialize, Serialize};
use crate::error::IdError;
use crate::id::multihash::Multihash;
pub use cid::Error as CidError;
pub const CODEC_DAG_CBOR: u64 = 0x71;
pub const CODEC_RAW: u64 = 0x55;
#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Cid(cid::CidGeneric<64>);
impl Cid {
#[must_use]
pub const fn new(codec: u64, hash: Multihash) -> Self {
Self(cid::Cid::new_v1(codec, hash.into_inner()))
}
#[must_use]
pub const fn codec(&self) -> u64 {
self.0.codec()
}
#[must_use]
pub fn multihash(&self) -> Multihash {
Multihash::from(*self.0.hash())
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, IdError> {
cid::CidGeneric::<64>::try_from(bytes)
.map(Self)
.map_err(|source| IdError::Cid { source })
}
pub fn parse_str(s: &str) -> Result<Self, IdError> {
cid::CidGeneric::<64>::from_str(s)
.map(Self)
.map_err(|source| IdError::Cid { source })
}
}
impl fmt::Display for Cid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl fmt::Debug for Cid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Cid({self})")
}
}
impl From<cid::CidGeneric<64>> for Cid {
fn from(c: cid::CidGeneric<64>) -> Self {
Self(c)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dag_cbor_cid_round_trips() {
let hash = Multihash::sha2_256(b"content");
let original = Cid::new(CODEC_DAG_CBOR, hash);
assert_eq!(original.codec(), CODEC_DAG_CBOR);
let bytes = original.to_bytes();
let decoded = Cid::from_bytes(&bytes).expect("decode");
assert_eq!(original, decoded);
let s = original.to_string();
assert!(s.starts_with('b'), "expected multibase b prefix, got {s}");
let decoded_str = Cid::parse_str(&s).expect("parse string");
assert_eq!(original, decoded_str);
}
#[test]
fn different_codecs_produce_distinct_cids() {
let hash = Multihash::sha2_256(b"content");
let dag = Cid::new(CODEC_DAG_CBOR, hash.clone());
let raw = Cid::new(CODEC_RAW, hash);
assert_ne!(dag, raw);
assert_eq!(dag.multihash(), raw.multihash());
assert_ne!(dag.codec(), raw.codec());
}
#[test]
fn different_content_distinct_cids() {
let a = Cid::new(CODEC_DAG_CBOR, Multihash::sha2_256(b"a"));
let b = Cid::new(CODEC_DAG_CBOR, Multihash::sha2_256(b"b"));
assert_ne!(a, b);
}
}