wnfs_unixfs_file/
types.rs

1use crate::{codecs::Codec, parse_links, protobufs};
2use anyhow::{anyhow, Result};
3use bytes::Bytes;
4use libipld::Cid;
5use std::{io::Cursor, pin::Pin};
6use tokio::io::AsyncRead;
7use wnfs_common::BlockStore;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct Block {
11    codec: Codec,
12    data: Bytes,
13    links: Vec<Cid>,
14}
15
16impl Block {
17    pub fn new(codec: Codec, data: Bytes, links: Vec<Cid>) -> Self {
18        Self { codec, data, links }
19    }
20
21    pub fn codec(&self) -> &Codec {
22        &self.codec
23    }
24
25    pub fn data(&self) -> &Bytes {
26        &self.data
27    }
28
29    pub fn links(&self) -> &[Cid] {
30        &self.links
31    }
32
33    pub fn raw_data_size(&self) -> Option<u64> {
34        match self.codec {
35            Codec::Raw => Some(self.data.len() as u64),
36            _ => None,
37        }
38    }
39
40    pub async fn store(&self, store: &impl BlockStore) -> Result<Cid> {
41        Ok(store
42            .put_block(self.data.clone(), self.codec.into())
43            .await?)
44    }
45
46    /// Validate the block. Will return an error if the links are wrong.
47    pub fn validate(&self) -> Result<()> {
48        // check that the links are complete
49        let expected_links = parse_links(self.codec, &self.data)?;
50        let mut actual_links = self.links.clone();
51        actual_links.sort();
52        // We need to deduplicate the actual links. An example case:
53        // A unixfs file which is 1GB of zeros.
54        // In that case, a node's links will contain multiple regions that hash to the same value,
55        // resulting in the same hash for whole ranges.
56        // But the parse_links function only accumulates a set of links.
57        actual_links.dedup();
58        anyhow::ensure!(expected_links == actual_links, "links do not match");
59        Ok(())
60    }
61
62    pub fn into_parts(self) -> (Codec, Bytes, Vec<Cid>) {
63        (self.codec, self.data, self.links)
64    }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct Link {
69    pub cid: Cid,
70    pub name: Option<String>,
71    pub tsize: Option<u64>,
72}
73
74impl Link {
75    pub fn as_ref(&self) -> LinkRef<'_> {
76        LinkRef {
77            cid: self.cid,
78            name: self.name.as_deref(),
79            tsize: self.tsize,
80        }
81    }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub struct LinkRef<'a> {
86    pub cid: Cid,
87    pub name: Option<&'a str>,
88    pub tsize: Option<u64>,
89}
90
91impl LinkRef<'_> {
92    pub fn to_owned(&self) -> Link {
93        Link {
94            cid: self.cid,
95            name: self.name.map(|t| t.to_string()),
96            tsize: self.tsize,
97        }
98    }
99}
100
101#[derive(Debug)]
102pub enum Links<'a> {
103    Leaf,
104    Node(PbLinks<'a>),
105}
106
107#[derive(Debug)]
108pub struct PbLinks<'a> {
109    i: usize,
110    outer: &'a protobufs::PbNode,
111}
112
113impl<'a> PbLinks<'a> {
114    pub fn new(outer: &'a protobufs::PbNode) -> Self {
115        PbLinks { i: 0, outer }
116    }
117}
118
119impl<'a> Iterator for Links<'a> {
120    type Item = Result<LinkRef<'a>>;
121
122    fn next(&mut self) -> Option<Self::Item> {
123        match self {
124            Links::Leaf => None,
125            Links::Node(links) => links.next(),
126        }
127    }
128
129    fn size_hint(&self) -> (usize, Option<usize>) {
130        match self {
131            Links::Leaf => (0, Some(0)),
132            Links::Node(links) => links.size_hint(),
133        }
134    }
135}
136
137impl<'a> Iterator for PbLinks<'a> {
138    type Item = Result<LinkRef<'a>>;
139
140    fn next(&mut self) -> Option<Self::Item> {
141        if self.i == self.outer.links.len() {
142            return None;
143        }
144
145        let l = &self.outer.links[self.i];
146        self.i += 1;
147
148        let res = l
149            .hash
150            .as_ref()
151            .ok_or_else(|| anyhow!("missing link"))
152            .and_then(|c| {
153                Ok(LinkRef {
154                    cid: Cid::read_bytes(Cursor::new(c))?,
155                    name: l.name.as_deref(),
156                    tsize: l.tsize,
157                })
158            });
159
160        Some(res)
161    }
162
163    fn size_hint(&self) -> (usize, Option<usize>) {
164        (self.outer.links.len(), Some(self.outer.links.len()))
165    }
166}
167
168#[cfg(not(target_arch = "wasm32"))]
169pub type BoxAsyncRead<'a> = Pin<Box<dyn AsyncRead + Send + 'a>>;
170
171#[cfg(target_arch = "wasm32")]
172pub type BoxAsyncRead<'a> = Pin<Box<dyn AsyncRead + 'a>>;