1use std::collections::BTreeMap;
2
3use cid::Cid;
4use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
5
6use crate::{
7 codec::Encoder,
8 error::CarError,
9 pb::unixfs::Data,
10 unixfs::{FileType, UnixFs, Link},
11 Decoder, Ipld,
12};
13
14impl Decoder<UnixFs> for Ipld {
15 fn decode(&self) -> Result<UnixFs, CarError> {
16 match self {
17 ipld::Ipld::Map(ref m) => {
18 let mut unix_fs: UnixFs = if let Some(ipld::Ipld::Bytes(data)) = m.get("Data") {
19 let mut reader = BytesReader::from_bytes(data);
20 Data::from_reader(&mut reader, data)
21 .map(|d| d.into())
22 .map_err(|e| CarError::Parsing(e.to_string()))?
23 } else {
24 return Err(CarError::Parsing("ipld format error".into()));
25 };
26 if let Some(ipld::Ipld::List(links)) = m.get("Links") {
27 links.iter().for_each(|l| if let ipld::Ipld::Map(ref m) = l {
28 let cid = if let Some(ipld::Ipld::Link(cid)) = m.get("Hash") {
29 *cid
30 } else {
31 return;
32 };
33 let name = if let Some(ipld::Ipld::String(name)) = m.get("Name") {
34 name.clone()
35 } else {
36 String::new()
37 };
38 let size = if let Some(ipld::Ipld::Integer(size)) = m.get("Tsize") {
39 *size as u64
40 } else {
41 0
42 };
43 unix_fs.add_link(Link::new(cid, name, size));
44 });
45 }
46 Ok(unix_fs)
47 }
48 _ => Err(CarError::Parsing("Not unixfs format".into())),
49 }
50 }
51}
52
53impl TryFrom<Ipld> for UnixFs {
54 type Error = CarError;
55
56 fn try_from(value: Ipld) -> Result<Self, Self::Error> {
57 value.decode()
58 }
59}
60
61impl TryFrom<(Cid, Ipld)> for UnixFs {
62 type Error = CarError;
63
64 fn try_from(value: (Cid, Ipld)) -> Result<Self, Self::Error> {
65 value.1.decode().map(|mut v| {
66 v.cid = Some(value.0);
67 v
68 })
69 }
70}
71
72fn convert_to_ipld(value: &Link) -> Result<Ipld, CarError> {
73 let mut map: BTreeMap<String, Ipld> = BTreeMap::new();
74 map.insert("Hash".to_string(), Ipld::Link(value.hash));
75 let file_name: Ipld = Ipld::String(
76 value.name_ref().into()
77 );
78 let tsize = Ipld::Integer(value.tsize as i128);
79 map.insert("Name".to_string(), file_name);
80 map.insert("Tsize".to_string(), tsize);
81 Ok(Ipld::Map(map))
82}
83
84impl Encoder<Ipld> for UnixFs {
85 fn encode(&self) -> Result<Ipld, CarError> {
86 match self.file_type {
87 FileType::Directory | FileType::File => {
88 let mut map = BTreeMap::new();
89 let data = Data {
90 mode: self.mode,
91 fanout: self.fanout,
92 hashType: self.hash_type,
93 filesize: self.file_size,
94 Type: self.file_type.into(),
95 blocksizes: self.block_sizes.clone(),
96 mtime: self.mtime().map(|s| s.clone().into()),
97 ..Default::default()
98 };
99 let mut buf: Vec<u8> = Vec::new();
100 let mut bw = Writer::new(&mut buf);
101 data.write_message(&mut bw)
102 .map_err(|e| CarError::Parsing(e.to_string()))?;
103 map.insert("Data".into(), Ipld::Bytes(buf));
104 let mut children_ipld: Vec<Ipld> = Vec::new();
105 for child in self.links.iter() {
106 children_ipld.push(convert_to_ipld(child)?);
107 }
108 map.insert("Links".to_string(), Ipld::List(children_ipld));
109 Ok(Ipld::Map(map))
110 }
111 _ => Err(CarError::Parsing("Not support unixfs format".into())),
112 }
113 }
114}
115
116impl TryFrom<UnixFs> for Ipld {
117 type Error = CarError;
118
119 fn try_from(value: UnixFs) -> Result<Self, Self::Error> {
120 value.encode()
121 }
122}