1use {
2 super::error::Error,
3 crate::{
4 alloc::{borrow::ToOwned, string::ToString},
5 cid::Cid,
6 dag,
7 },
8 alloc::vec::Vec,
9 serde::{Deserialize, Serialize},
10};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14#[non_exhaustive]
15pub enum CarHeader {
16 V1(CarHeaderV1),
17}
18
19impl CarHeader {
20 pub fn new_v1(roots: Vec<Cid>) -> Self {
21 Self::V1(roots.into())
22 }
23
24 pub fn decode(buffer: &[u8]) -> Result<Self, Error> {
25 let header: CarHeaderV1 =
26 dag::from_slice(buffer).map_err(|e| Error::Parsing(e.to_string()))?;
27
28 if header.roots.is_empty() {
29 return Err(Error::Parsing("empty CAR file".to_owned()));
30 }
31
32 if header.version != 1 {
33 return Err(Error::InvalidFile(
34 "Only CAR file version 1 is supported".to_string(),
35 ));
36 }
37
38 Ok(CarHeader::V1(header))
39 }
40
41 pub fn encode(&self) -> Result<Vec<u8>, Error> {
42 match self {
43 CarHeader::V1(ref header) => {
44 let res = dag::to_vec(header).expect("vec");
45 Ok(res)
46 }
47 }
48 }
49
50 pub fn roots(&self) -> &[Cid] {
51 match self {
52 CarHeader::V1(header) => &header.roots,
53 }
54 }
55
56 pub fn version(&self) -> u64 {
57 match self {
58 CarHeader::V1(_) => 1,
59 }
60 }
61}
62
63#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
65pub struct CarHeaderV1 {
66 pub roots: Vec<Cid>,
67 pub version: u64,
68}
69
70impl CarHeaderV1 {
71 pub fn new(roots: Vec<Cid>, version: u64) -> Self {
73 Self { roots, version }
74 }
75}
76
77impl From<Vec<Cid>> for CarHeaderV1 {
78 fn from(roots: Vec<Cid>) -> Self {
79 Self { roots, version: 1 }
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use {super::*, crate::multihash::Multihash, ::alloc::*};
86
87 #[test]
88 fn symmetric_header_v1() {
89 let digest =
90 Multihash::wrap(0x1e, blake3::hash(b"test").as_bytes()).unwrap();
91 let cid = Cid::new_v1(0x71, digest);
92
93 let header = CarHeaderV1::from(vec![cid]);
94
95 let bytes = dag::to_vec(&header).unwrap();
96
97 assert_eq!(dag::from_slice::<CarHeaderV1>(&bytes).unwrap(), header);
98 }
99}