ipld_nostd/car/
header.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use {
	super::error::Error,
	crate::{
		alloc::{borrow::ToOwned, string::ToString},
		cid::Cid,
		dag,
	},
	alloc::vec::Vec,
	serde::{Deserialize, Serialize},
};

/// A car header.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum CarHeader {
	V1(CarHeaderV1),
}

impl CarHeader {
	pub fn new_v1(roots: Vec<Cid>) -> Self {
		Self::V1(roots.into())
	}

	pub fn decode(buffer: &[u8]) -> Result<Self, Error> {
		let header: CarHeaderV1 =
			dag::from_slice(buffer).map_err(|e| Error::Parsing(e.to_string()))?;

		if header.roots.is_empty() {
			return Err(Error::Parsing("empty CAR file".to_owned()));
		}

		if header.version != 1 {
			return Err(Error::InvalidFile(
				"Only CAR file version 1 is supported".to_string(),
			));
		}

		Ok(CarHeader::V1(header))
	}

	pub fn encode(&self) -> Result<Vec<u8>, Error> {
		match self {
			CarHeader::V1(ref header) => {
				let res = dag::to_vec(header).expect("vec");
				Ok(res)
			}
		}
	}

	pub fn roots(&self) -> &[Cid] {
		match self {
			CarHeader::V1(header) => &header.roots,
		}
	}

	pub fn version(&self) -> u64 {
		match self {
			CarHeader::V1(_) => 1,
		}
	}
}

/// CAR file header version 1.
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct CarHeaderV1 {
	pub roots: Vec<Cid>,
	pub version: u64,
}

impl CarHeaderV1 {
	/// Creates a new CAR file header
	pub fn new(roots: Vec<Cid>, version: u64) -> Self {
		Self { roots, version }
	}
}

impl From<Vec<Cid>> for CarHeaderV1 {
	fn from(roots: Vec<Cid>) -> Self {
		Self { roots, version: 1 }
	}
}

#[cfg(test)]
mod tests {
	use {super::*, crate::multihash::Multihash, ::alloc::*};

	#[test]
	fn symmetric_header_v1() {
		let digest =
			Multihash::wrap(0x1e, blake3::hash(b"test").as_bytes()).unwrap();
		let cid = Cid::new_v1(0x71, digest);

		let header = CarHeaderV1::from(vec![cid]);

		let bytes = dag::to_vec(&header).unwrap();

		assert_eq!(dag::from_slice::<CarHeaderV1>(&bytes).unwrap(), header);
	}
}