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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::collections::BTreeMap;

use cid::Cid;
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};

use crate::{
    codec::Encoder,
    error::CarError,
    pb::unixfs::Data,
    unixfs::{FileType, Link, UnixFs},
    Decoder, Ipld,
};

impl Decoder<UnixFs> for Ipld {
    fn decode(&self) -> Result<UnixFs, CarError> {
        match self {
            ipld::Ipld::Map(ref m) => {
                let mut unix_fs: UnixFs = if let Some(ipld::Ipld::Bytes(data)) = m.get("Data") {
                    let mut reader = BytesReader::from_bytes(data);
                    Data::from_reader(&mut reader, data)
                        .map(|d| d.into())
                        .map_err(|e| CarError::Parsing(e.to_string()))?
                } else {
                    return Err(CarError::Parsing("ipld format error".into()));
                };
                if let Some(ipld::Ipld::List(links)) = m.get("Links") {
                    links.iter().for_each(|l| {
                        if let ipld::Ipld::Map(ref m) = l {
                            let cid = if let Some(ipld::Ipld::Link(cid)) = m.get("Hash") {
                                *cid
                            } else {
                                return;
                            };
                            let name = if let Some(ipld::Ipld::String(name)) = m.get("Name") {
                                name.clone()
                            } else {
                                String::new()
                            };
                            let size = if let Some(ipld::Ipld::Integer(size)) = m.get("Tsize") {
                                *size as u64
                            } else {
                                0
                            };
                            unix_fs.add_link(Link {
                                hash: cid,
                                file_type: FileType::Raw,
                                name,
                                tsize: size,
                            });
                        }
                    });
                }
                Ok(unix_fs)
            }
            _ => Err(CarError::Parsing("Not unixfs format".into())),
        }
    }
}

impl TryFrom<Ipld> for UnixFs {
    type Error = CarError;

    fn try_from(value: Ipld) -> Result<Self, Self::Error> {
        value.decode()
    }
}

impl TryFrom<(Cid, Ipld)> for UnixFs {
    type Error = CarError;

    fn try_from(value: (Cid, Ipld)) -> Result<Self, Self::Error> {
        value.1.decode().map(|mut v| {
            v.cid = Some(value.0);
            v
        })
    }
}

fn convert_to_ipld(value: &Link) -> Result<Ipld, CarError> {
    let mut map: BTreeMap<String, Ipld> = BTreeMap::new();
    map.insert("Hash".to_string(), Ipld::Link(value.hash));
    let file_name: Ipld = Ipld::String(value.name.to_owned());
    let tsize = Ipld::Integer(value.tsize as i128);
    map.insert("Name".to_string(), file_name);
    map.insert("Tsize".to_string(), tsize);
    Ok(Ipld::Map(map))
}

impl Encoder<Ipld> for UnixFs {
    fn encode(&self) -> Result<Ipld, CarError> {
        match self.file_type {
            FileType::Directory | FileType::File => {
                let mut map = BTreeMap::new();
                let data = Data {
                    mode: self.mode,
                    fanout: self.fanout,
                    hashType: self.hash_type,
                    filesize: self.file_size,
                    Type: self.file_type.into(),
                    blocksizes: self.block_sizes.clone(),
                    mtime: self.mtime().map(|s| s.clone().into()),
                    ..Default::default()
                };
                let mut buf: Vec<u8> = Vec::new();
                let mut bw = Writer::new(&mut buf);
                data.write_message(&mut bw)
                    .map_err(|e| CarError::Parsing(e.to_string()))?;
                map.insert("Data".into(), Ipld::Bytes(buf));
                let mut children_ipld: Vec<Ipld> = Vec::new();
                for child in self.links.iter() {
                    children_ipld.push(convert_to_ipld(child)?);
                }
                map.insert("Links".to_string(), Ipld::List(children_ipld));
                Ok(Ipld::Map(map))
            }
            _ => Err(CarError::Parsing("Not support unixfs format".into())),
        }
    }
}

impl TryFrom<UnixFs> for Ipld {
    type Error = CarError;

    fn try_from(value: UnixFs) -> Result<Self, Self::Error> {
        value.encode()
    }
}