ipld_nostd/car/
reader.rs

1use {
2	super::{
3		error::Error,
4		header::CarHeader,
5		util::{ld_read, read_node},
6	},
7	crate::cid::Cid,
8	alloc::{string::ToString, vec::Vec},
9};
10
11/// Reads CAR files that are in a BufReader
12#[derive(Debug)]
13pub struct CarReader<R> {
14	reader: R,
15	header: CarHeader,
16	buffer: Vec<u8>,
17}
18
19impl<R> CarReader<R>
20where
21	R: core2::io::Read,
22{
23	/// Creates a new CarReader and parses the CarHeader
24	pub fn new(mut reader: R) -> Result<Self, Error> {
25		let mut buffer = Vec::new();
26
27		match ld_read(&mut reader, &mut buffer)? {
28			Some(buf) => {
29				let header = CarHeader::decode(buf)?;
30
31				Ok(CarReader {
32					reader,
33					header,
34					buffer,
35				})
36			}
37			None => Err(Error::Parsing(
38				"failed to parse uvarint for header".to_string(),
39			)),
40		}
41	}
42
43	/// Returns the header of this car file.
44	pub fn header(&self) -> &CarHeader {
45		&self.header
46	}
47
48	/// Returns the next IPLD Block in the buffer
49	pub fn next_block(&mut self) -> Result<Option<(Cid, Vec<u8>)>, Error> {
50		read_node(&mut self.reader, &mut self.buffer)
51	}
52}
53
54impl<R: core2::io::Read> IntoIterator for CarReader<R> {
55	type IntoIter = Iter<R>;
56	type Item = Result<(Cid, Vec<u8>), Error>;
57
58	fn into_iter(self) -> Self::IntoIter {
59		Iter(self)
60	}
61}
62
63pub struct Iter<R: core2::io::Read>(CarReader<R>);
64impl<R: core2::io::Read> Iterator for Iter<R> {
65	type Item = Result<(Cid, Vec<u8>), Error>;
66
67	fn next(&mut self) -> Option<Self::Item> {
68		read_node(&mut self.0.reader, &mut self.0.buffer).transpose()
69	}
70}
71
72#[cfg(test)]
73mod tests {
74	use {
75		super::super::{header::CarHeaderV1, writer::CarWriter, *},
76		crate::{cid::Cid, multihash::Multihash},
77		::alloc::{vec::Vec, *},
78		core2::io::Cursor,
79		itertools::Itertools,
80	};
81
82	#[test]
83	fn car_write_read() {
84		let digest_test =
85			Multihash::wrap(0x1e, blake3::hash(b"test").as_bytes()).unwrap();
86		let cid_test = Cid::new_v1(0x71, digest_test);
87		let digest_foo =
88			Multihash::wrap(0x1e, blake3::hash(b"foo").as_bytes()).unwrap();
89		let cid_foo = Cid::new_v1(0x71, digest_foo);
90
91		let header = CarHeader::V1(CarHeaderV1::from(vec![cid_foo]));
92
93		let mut buffer = Vec::new();
94		let mut writer = CarWriter::new(header, &mut buffer);
95		writer.write(cid_test, b"test").unwrap();
96		writer.write(cid_foo, b"foo").unwrap();
97		writer.finish().unwrap();
98
99		let reader = Cursor::new(&buffer);
100		let car_reader = CarReader::new(reader).unwrap();
101		let files: Vec<_> = car_reader.into_iter().try_collect().unwrap();
102
103		assert_eq!(files.len(), 2);
104		assert_eq!(files[0].0, cid_test);
105		assert_eq!(files[0].1, b"test");
106		assert_eq!(files[1].0, cid_foo);
107		assert_eq!(files[1].1, b"foo");
108	}
109}