diff options
Diffstat (limited to 'ipfs-api/src/client.rs')
-rw-r--r-- | ipfs-api/src/client.rs | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/ipfs-api/src/client.rs b/ipfs-api/src/client.rs index 92946ea..430612e 100644 --- a/ipfs-api/src/client.rs +++ b/ipfs-api/src/client.rs @@ -9,6 +9,7 @@ use futures::{ stream::{self, Stream}, Future, IntoFuture, + future, }; use header::TRAILER; use http::uri::InvalidUri; @@ -24,6 +25,7 @@ use response::{self, Error}; use serde::{Deserialize, Serialize}; use serde_json; use std::io::Read; +use std::path::{Path, PathBuf}; use tokio_codec::{Decoder, FramedRead}; /// A response returned by the HTTP client. @@ -351,6 +353,85 @@ impl IpfsClient { self.request(&request::Add, Some(form)) } + /// Add a path to Ipfs. Can be a file or directory. + /// A hard limit of 128 open file descriptors is set such + /// that any small additional files are stored in-memory. + /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # + /// use ipfs_api::IpfsClient; + /// + /// # fn main() { + /// let client = IpfsClient::default(); + /// let path = "./src"; + /// let req = client.add_path(path); + /// # } + /// ``` + /// + #[inline] + pub fn add_path<P>(&self, path: P) -> AsyncResponse<response::AddResponse> + where + P: AsRef<Path>, + { + let mut form = multipart::Form::default(); + + let prefix = path.as_ref().parent(); + + let mut paths_to_add: Vec<(PathBuf, u64)> = vec![]; + + for path in walkdir::WalkDir::new(path.as_ref()) { + match path { + Ok(entry) => { + if entry.file_type().is_file() { + let file_size = + entry.metadata().map(|metadata| metadata.len()).unwrap_or(0); + paths_to_add.push((entry.path().to_path_buf(), file_size)); + } + } + Err(err) => { + return Box::new(future::err(Error::Io(err.into()))); + } + } + } + + paths_to_add.sort_unstable_by(|(_, a), (_, b)| a.cmp(b).reverse()); + + let mut it = 0; + const FILE_DESCRIPTOR_LIMIT: usize = 127; + + for (path, file_size) in paths_to_add { + let file = std::fs::File::open(&path); + if file.is_err() { + return Box::new(future::err(file.unwrap_err().into())); + } + let file_name = match prefix { + Some(prefix) => path.strip_prefix(prefix).unwrap(), + None => path.as_path(), + } + .to_string_lossy(); + + if it < FILE_DESCRIPTOR_LIMIT { + form.add_reader_file("path", file.unwrap(), file_name); + it += 1; + } else { + let mut buf = Vec::with_capacity(file_size as usize); + if let Err(err) = file.unwrap().read_to_end(&mut buf) { + return Box::new(future::err(err.into())); + } + form.add_reader_file("path", std::io::Cursor::new(buf), file_name); + } + } + + Box::new( + self.request_stream_json(&request::Add, Some(form)) + .collect() + .map(|mut responses: Vec<response::AddResponse>| responses.pop().unwrap()), + ) + } + /// Returns the current ledger for a peer. /// /// # Examples |