blockless_car/
unixfs_codec.rs

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