1#[cfg(not(target_arch = "wasm32"))]
7use std::path::PathBuf;
8
9use anyhow::Error;
10use bytes::Bytes;
11use futures::{
12 stream::{self, BoxStream},
13 StreamExt,
14};
15use ipld_core::cid::Cid;
16use ll::file::FileReadFailed;
17pub use rust_unixfs as ll;
18
19mod add;
20mod cat;
21mod get;
22mod ls;
23pub use add::UnixfsAdd;
24pub use cat::{StartingPoint, UnixfsCat};
25pub use get::UnixfsGet;
26pub use ls::{Entry, UnixfsLs};
27
28use crate::{
29 dag::{ResolveError, UnexpectedResolved},
30 Ipfs, IpfsPath,
31};
32
33pub struct IpfsUnixfs {
34 ipfs: Ipfs,
35}
36
37pub enum AddOpt {
38 #[cfg(not(target_arch = "wasm32"))]
39 Path(PathBuf),
40 Stream(BoxStream<'static, std::io::Result<Bytes>>),
41 StreamWithName(String, BoxStream<'static, std::io::Result<Bytes>>),
42}
43
44#[cfg(not(target_arch = "wasm32"))]
45impl From<&str> for AddOpt {
46 fn from(value: &str) -> Self {
47 AddOpt::Path(PathBuf::from(value))
48 }
49}
50
51#[cfg(not(target_arch = "wasm32"))]
52impl From<String> for AddOpt {
53 fn from(value: String) -> Self {
54 AddOpt::Path(PathBuf::from(value))
55 }
56}
57
58#[cfg(not(target_arch = "wasm32"))]
59impl From<&std::path::Path> for AddOpt {
60 fn from(path: &std::path::Path) -> Self {
61 AddOpt::Path(path.to_path_buf())
62 }
63}
64
65#[cfg(not(target_arch = "wasm32"))]
66impl From<PathBuf> for AddOpt {
67 fn from(path: PathBuf) -> Self {
68 AddOpt::Path(path)
69 }
70}
71
72impl From<Vec<u8>> for AddOpt {
73 fn from(bytes: Vec<u8>) -> Self {
74 let bytes: Bytes = bytes.into();
75 Self::from(bytes)
76 }
77}
78
79impl From<&'static [u8]> for AddOpt {
80 fn from(bytes: &'static [u8]) -> Self {
81 let bytes: Bytes = bytes.into();
82 Self::from(bytes)
83 }
84}
85
86impl From<(String, Vec<u8>)> for AddOpt {
87 fn from((name, bytes): (String, Vec<u8>)) -> Self {
88 let bytes: Bytes = bytes.into();
89 Self::from((name, bytes))
90 }
91}
92
93impl From<(String, &'static [u8])> for AddOpt {
94 fn from((name, bytes): (String, &'static [u8])) -> Self {
95 let bytes: Bytes = bytes.into();
96 Self::from((name, bytes))
97 }
98}
99
100impl From<Bytes> for AddOpt {
101 fn from(bytes: Bytes) -> Self {
102 let stream = stream::once(async { Ok::<_, std::io::Error>(bytes) }).boxed();
103 AddOpt::Stream(stream)
104 }
105}
106
107impl From<(String, Bytes)> for AddOpt {
108 fn from((name, bytes): (String, Bytes)) -> Self {
109 let stream = stream::once(async { Ok::<_, std::io::Error>(bytes) }).boxed();
110 Self::from((name, stream))
111 }
112}
113
114impl From<BoxStream<'static, std::io::Result<Bytes>>> for AddOpt {
115 fn from(stream: BoxStream<'static, std::io::Result<Bytes>>) -> Self {
116 AddOpt::Stream(stream)
117 }
118}
119
120impl From<(String, BoxStream<'static, std::io::Result<Bytes>>)> for AddOpt {
121 fn from((name, stream): (String, BoxStream<'static, std::io::Result<Bytes>>)) -> Self {
122 AddOpt::StreamWithName(name, stream)
123 }
124}
125
126impl From<BoxStream<'static, std::io::Result<Vec<u8>>>> for AddOpt {
127 fn from(stream: BoxStream<'static, std::io::Result<Vec<u8>>>) -> Self {
128 AddOpt::Stream(stream.map(|result| result.map(|data| data.into())).boxed())
129 }
130}
131
132impl From<(String, BoxStream<'static, std::io::Result<Vec<u8>>>)> for AddOpt {
133 fn from((name, stream): (String, BoxStream<'static, std::io::Result<Vec<u8>>>)) -> Self {
134 let stream = stream.map(|result| result.map(|data| data.into())).boxed();
135 AddOpt::StreamWithName(name, stream)
136 }
137}
138
139impl IpfsUnixfs {
140 pub fn new(ipfs: Ipfs) -> Self {
141 Self { ipfs }
142 }
143
144 pub fn cat(&self, starting_point: impl Into<StartingPoint>) -> UnixfsCat {
148 UnixfsCat::with_ipfs(&self.ipfs, starting_point)
149 }
150
151 pub fn add<I: Into<AddOpt>>(&self, item: I) -> UnixfsAdd {
153 let item = item.into();
154 match item {
155 #[cfg(not(target_arch = "wasm32"))]
156 AddOpt::Path(path) => UnixfsAdd::with_ipfs(&self.ipfs, path),
157 AddOpt::Stream(stream) => UnixfsAdd::with_ipfs(
158 &self.ipfs,
159 add::AddOpt::Stream {
160 name: None,
161 total: None,
162 stream,
163 },
164 ),
165 AddOpt::StreamWithName(name, stream) => UnixfsAdd::with_ipfs(
166 &self.ipfs,
167 add::AddOpt::Stream {
168 name: Some(name),
169 total: None,
170 stream,
171 },
172 ),
173 }
174 }
175
176 pub fn get<I: Into<IpfsPath>, P: AsRef<std::path::Path>>(&self, path: I, dest: P) -> UnixfsGet {
180 UnixfsGet::with_ipfs(&self.ipfs, path, dest)
181 }
182
183 pub fn ls<I: Into<IpfsPath>>(&self, path: I) -> UnixfsLs {
185 UnixfsLs::with_ipfs(&self.ipfs, path)
186 }
187}
188
189#[derive(Debug)]
190pub enum UnixfsStatus {
191 ProgressStatus {
192 written: usize,
193 total_size: Option<usize>,
194 },
195 CompletedStatus {
196 path: IpfsPath,
197 written: usize,
198 total_size: Option<usize>,
199 },
200 FailedStatus {
201 written: usize,
202 total_size: Option<usize>,
203 error: Error,
204 },
205}
206
207#[derive(Debug, thiserror::Error)]
209pub enum TraversalFailed {
210 #[error("path resolving failed")]
212 Resolving(#[source] ResolveError),
213
214 #[error("path resolved to unexpected")]
217 Path(#[source] UnexpectedResolved),
218
219 #[error("loading of {} failed", .0)]
221 Loading(Cid, #[source] Error),
222
223 #[error("data exceeded max length")]
224 MaxLengthExceeded { size: usize, length: usize },
225 #[error("Timeout while resolving {path}")]
226 Timeout { path: IpfsPath },
227
228 #[error("walk failed on {}", .0)]
230 Walking(Cid, #[source] FileReadFailed),
231
232 #[error(transparent)]
233 Io(std::io::Error),
234}
235
236#[cfg(test)]
237mod tests {
238 #[test]
239 fn test_file_cid() {
240 let content = "\u{8}\u{2}\u{12}\u{12}Here is some data\n\u{18}\u{12}";
245
246 let mut adder = rust_unixfs::file::adder::FileAdder::default();
247 let (mut blocks, consumed) = adder.push(content.as_bytes());
248 assert_eq!(consumed, content.len(), "should had consumed all content");
249 assert_eq!(
250 blocks.next(),
251 None,
252 "should not had produced any blocks yet"
253 );
254
255 let mut blocks = adder.finish();
256
257 let (cid, _block) = blocks.next().unwrap();
258 assert_eq!(blocks.next(), None, "should had been the last");
259
260 assert_eq!(
261 "QmQZE72h2Vdm3F5gWr9RLuzSw3rUJEkKedWEa8t8XVygT5",
262 cid.to_string(),
263 "matches cid from go-ipfs 0.6.0"
264 );
265 }
266}