diff options
Diffstat (limited to 'ipfs-api-prelude/src/api.rs')
-rw-r--r-- | ipfs-api-prelude/src/api.rs | 2098 |
1 files changed, 2098 insertions, 0 deletions
diff --git a/ipfs-api-prelude/src/api.rs b/ipfs-api-prelude/src/api.rs new file mode 100644 index 0000000..48ed893 --- /dev/null +++ b/ipfs-api-prelude/src/api.rs @@ -0,0 +1,2098 @@ +// Copyright 2021 rust-ipfs-api Developers +// +// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or +// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or +// http://opensource.org/licenses/MIT>, at your option. This file may not be +// copied, modified, or distributed except according to those terms. +// + +use crate::{read::LineDecoder, request, response, Backend}; +use async_trait::async_trait; +use bytes::Bytes; +use common_multipart_rfc7578::client::multipart; +use futures::{future, FutureExt, Stream, TryStreamExt}; +use std::{ + fs::File, + io::{Cursor, Read}, + path::{Path, PathBuf}, +}; + +const FILE_DESCRIPTOR_LIMIT: usize = 127; + +// Implements a call to the IPFS that returns a streaming body response. +// Implementing this in a macro is necessary because the Rust compiler +// can't reason about the lifetime of the request instance properly. It +// thinks that the request needs to live as long as the returned stream, +// but in reality, the request instance is only used to build the Hyper +// or Actix request. +// +macro_rules! impl_stream_api_response { + (($self:ident, $req:expr, $form:expr) => $call:ident) => { + impl_stream_api_response! { + ($self, $req, $form) |req| => { $self.$call(req) } + } + }; + (($self:ident, $req:expr, $form:expr) |$var:ident| => $impl:block) => { + match $self.build_base_request(&$req, $form) { + Ok($var) => $impl, + Err(e) => Box::new(future::err(e).into_stream()), + } + }; +} + +#[async_trait(?Send)] +pub trait IpfsApi: Backend { + /// Add file to Ipfs. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// use std::io::Cursor; + /// + /// let client = IpfsClient::default(); + /// let data = Cursor::new("Hello World!"); + /// let res = client.add(data); + /// ``` + /// + async fn add<R>(&self, data: R) -> Result<response::AddResponse, Self::Error> + where + R: 'static + Read + Send + Sync, + { + self.add_with_options(data, request::Add::default()).await + } + + /// Add a file to IPFS with options. + /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// use std::io::Cursor; + /// + /// # fn main() { + /// let client = IpfsClient::default(); + /// let data = Cursor::new("Hello World!"); + /// #[cfg(feature = "with-builder")] + /// let add = ipfs_api::request::Add::builder() + /// .chunker("rabin-512-1024-2048") + /// .build(); + /// #[cfg(not(feature = "with-builder"))] + /// let add = ipfs_api::request::Add { + /// chunker: Some("rabin-512-1024-2048"), + /// ..Default::default() + /// }; + /// let req = client.add_with_options(data, add); + /// # } + /// ``` + /// + async fn add_with_options<R>( + &self, + data: R, + add: request::Add<'_>, + ) -> Result<response::AddResponse, Self::Error> + where + R: 'static + Read + Send + Sync, + { + let mut form = multipart::Form::default(); + + form.add_reader("path", data); + + self.request(add, Some(form)).await + } + + /// 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 + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let path = "./src"; + /// let res = client.add_path(path); + /// ``` + /// + async fn add_path<P>(&self, path: P) -> Result<Vec<response::AddResponse>, Self::Error> + where + P: AsRef<Path>, + { + 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()) + .map_err(|e| crate::Error::Io(e.into()))?; + + paths_to_add.push((entry.path().to_path_buf(), file_size)); + } + Ok(_) => (), + Err(e) => return Err(crate::Error::Io(e.into()).into()), + } + } + + paths_to_add.sort_unstable_by(|(_, a), (_, b)| a.cmp(b).reverse()); + + let mut it = 0; + let mut form = multipart::Form::default(); + + for (path, file_size) in paths_to_add { + let mut file = File::open(&path).map_err(crate::Error::Io)?; + 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, file_name); + + it += 1; + } else { + let mut buf = Vec::with_capacity(file_size as usize); + let _ = file.read_to_end(&mut buf).map_err(crate::Error::Io)?; + + form.add_reader_file("path", Cursor::new(buf), file_name); + } + } + + let req = self.build_base_request(&request::Add::default(), Some(form))?; + + self.request_stream_json(req).try_collect().await + } + + /// Returns the current ledger for a peer. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.bitswap_ledger("QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"); + /// ``` + /// + async fn bitswap_ledger( + &self, + peer: &str, + ) -> Result<response::BitswapLedgerResponse, Self::Error> { + self.request(request::BitswapLedger { peer }, None).await + } + + /// Triggers a reprovide. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.bitswap_reprovide(); + /// ``` + /// + async fn bitswap_reprovide(&self) -> Result<response::BitswapReprovideResponse, Self::Error> { + self.request_empty(request::BitswapReprovide, None).await + } + + /// Returns some stats about the bitswap agent. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.bitswap_stat(); + /// ``` + /// + async fn bitswap_stat(&self) -> Result<response::BitswapStatResponse, Self::Error> { + self.request(request::BitswapStat, None).await + } + + /// Remove a given block from your wantlist. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.bitswap_unwant("QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"); + /// ``` + /// + async fn bitswap_unwant( + &self, + key: &str, + ) -> Result<response::BitswapUnwantResponse, Self::Error> { + self.request_empty(request::BitswapUnwant { key }, None) + .await + } + + /// Shows blocks on the wantlist for you or the specified peer. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.bitswap_wantlist( + /// Some("QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ") + /// ); + /// ``` + /// + async fn bitswap_wantlist( + &self, + peer: Option<&str>, + ) -> Result<response::BitswapWantlistResponse, Self::Error> { + self.request(request::BitswapWantlist { peer }, None).await + } + + /// Gets a raw IPFS block. + /// + /// # Examples + /// + /// ```no_run + /// use futures::TryStreamExt; + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let hash = "QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"; + /// let res = client + /// .block_get(hash) + /// .map_ok(|chunk| chunk.to_vec()) + /// .try_concat(); + /// ``` + /// + fn block_get(&self, hash: &str) -> Box<dyn Stream<Item = Result<Bytes, Self::Error>> + Unpin> { + impl_stream_api_response! { + (self, request::BlockGet { hash }, None) => request_stream_bytes + } + } + + /// Store input as an IPFS block. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// use std::io::Cursor; + /// + /// let client = IpfsClient::default(); + /// let data = Cursor::new("Hello World!"); + /// let res = client.block_put(data); + /// ``` + /// + async fn block_put<R>(&self, data: R) -> Result<response::BlockPutResponse, Self::Error> + where + R: 'static + Read + Send + Sync, + { + let mut form = multipart::Form::default(); + + form.add_reader("data", data); + + self.request(request::BlockPut, Some(form)).await + } + + /// Removes an IPFS block. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.block_rm("QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"); + /// ``` + /// + async fn block_rm(&self, hash: &str) -> Result<response::BlockRmResponse, Self::Error> { + self.request(request::BlockRm { hash }, None).await + } + + /// Prints information about a raw IPFS block. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.block_stat("QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"); + /// ``` + /// + async fn block_stat(&self, hash: &str) -> Result<response::BlockStatResponse, Self::Error> { + self.request(request::BlockStat { hash }, None).await + } + + /// Add default peers to the bootstrap list. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.bootstrap_add_default(); + /// ``` + /// + async fn bootstrap_add_default( + &self, + ) -> Result<response::BootstrapAddDefaultResponse, Self::Error> { + self.request(request::BootstrapAddDefault, None).await + } + + /// Lists peers in bootstrap list. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.bootstrap_list(); + /// ``` + /// + async fn bootstrap_list(&self) -> Result<response::BootstrapListResponse, Self::Error> { + self.request(request::BootstrapList, None).await + } + + /// Removes all peers in bootstrap list. + /// + /// # Examples + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.bootstrap_rm_all(); + /// ``` + /// + async fn bootstrap_rm_all(&self) -> Result<response::BootstrapRmAllResponse, Self::Error> { + self.request(request::BootstrapRmAll, None).await + } + + /// Returns the contents of an Ipfs object. + /// + /// # Examples + /// + /// ```no_run + /// use futures::TryStreamExt; + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let hash = "QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"; + /// let res = client + /// .cat(hash) + /// .map_ok(|chunk| chunk.to_vec()) + /// .try_concat(); + /// ``` + /// + fn cat(&self, path: &str) -> Box<dyn Stream<Item = Result<Bytes, Self::Error>> + Unpin> { + impl_stream_api_response! { + (self, request::Cat { path }, None) => request_stream_bytes + } + } + + /// List available commands that the server accepts. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.commands(); + /// ``` + /// + async fn commands(&self) -> Result<response::CommandsResponse, Self::Error> { + self.request(request::Commands, None).await + } + + /// Get ipfs config strings. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.config_get_string("Identity.PeerID"); + /// ``` + /// + async fn config_get_string(&self, key: &str) -> Result<response::ConfigResponse, Self::Error> { + self.request( + request::Config { + key, + value: None, + boolean: None, + stringified_json: None, + }, + None, + ) + .await + } + + /// Get ipfs config booleans. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.config_get_bool("Datastore.HashOnRead"); + /// ``` + /// + async fn config_get_bool(&self, key: &str) -> Result<response::ConfigResponse, Self::Error> { + self.request( + request::Config { + key, + value: None, + boolean: None, + stringified_json: None, + }, + None, + ) + .await + } + + /// Get ipfs config json. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.config_get_json("Mounts"); + /// ``` + /// + async fn config_get_json(&self, key: &str) -> Result<response::ConfigResponse, Self::Error> { + self.request( + request::Config { + key, + value: None, + boolean: None, + stringified_json: None, + }, + None, + ) + .await + } + + /// Set ipfs config string. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.config_set_string("Routing.Type", "dht"); + /// ``` + /// + async fn config_set_string( + &self, + key: &str, + value: &str, + ) -> Result<response::ConfigResponse, Self::Error> { + self.request( + request::Config { + key, + value: Some(value), + boolean: None, + stringified_json: None, + }, + None, + ) + .await + } + + /// Set ipfs config boolean. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.config_set_bool("Pubsub.DisableSigning", false); + /// ``` + /// + async fn config_set_bool( + &self, + key: &str, + value: bool, + ) -> Result<response::ConfigResponse, Self::Error> { + self.request( + request::Config { + key, + value: Some(&value.to_string()), + boolean: Some(true), + stringified_json: None, + }, + None, + ) + .await + } + + /// Set ipfs config json. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.config_set_json("Discovery", r#"{"MDNS":{"Enabled":true,"Interval":10}}"#); + /// ``` + /// + async fn config_set_json( + &self, + key: &str, + value: &str, + ) -> Result<response::ConfigResponse, Self::Error> { + self.request( + request::Config { + key, + value: Some(value), + boolean: None, + stringified_json: Some(true), + }, + None, + ) + .await + } + + /// Opens the config file for editing (on the server). + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.config_edit(); + /// ``` + /// + async fn config_edit(&self) -> Result<response::ConfigEditResponse, Self::Error> { + self.request(request::ConfigEdit, None).await + } + + /// Replace the config file. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// use std::io::Cursor; + /// + /// let client = IpfsClient::default(); + /// let config = Cursor::new("{..json..}"); + /// let res = client.config_replace(config); + /// ``` + /// + async fn config_replace<R>( + &self, + data: R, + ) -> Result<response::ConfigReplaceResponse, Self::Error> + where + R: 'static + Read + Send + Sync, + { + let mut form = multipart::Form::default(); + + form.add_reader("file", data); + + self.request_empty(request::ConfigReplace, Some(form)).await + } + + /// Show the current config of the server. + /// + /// Returns an unparsed json string, due to an unclear spec. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.config_show(); + /// ``` + /// + async fn config_show(&self) -> Result<response::ConfigShowResponse, Self::Error> { + self.request_string(request::ConfigShow, None).await + } + + /// Returns information about a dag node in Ipfs. + /// + /// ```no_run + /// use futures::TryStreamExt; + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let hash = "QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"; + /// let res = client + /// .dag_get(hash) + /// .map_ok(|chunk| chunk.to_vec()) + /// .try_concat(); + /// ``` + /// + fn dag_get(&self, path: &str) -> Box<dyn Stream<Item = Result<Bytes, Self::Error>> + Unpin> { + impl_stream_api_response! { + (self, request::DagGet { path }, None) => request_stream_bytes + } + } + + /// Add a DAG node to Ipfs. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// use std::io::Cursor; + /// + /// let client = IpfsClient::default(); + /// let data = Cursor::new(r#"{ "hello" : "world" }"#); + /// let res = client.dag_put(data); + /// ``` + /// + async fn dag_put<R>(&self, data: R) -> Result<response::DagPutResponse, Self::Error> + where + R: 'static + Read + Send + Sync, + { + let mut form = multipart::Form::default(); + + form.add_reader("object data", data); + + self.request(request::DagPut, Some(form)).await + } + + // TODO /dag/resolve + + /// Query the DHT for all of the multiaddresses associated with a Peer ID. + /// + /// ```no_run + /// use futures::TryStreamExt; + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let peer = "QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM"; + /// let res = client.dht_findpeer(peer).try_collect::<Vec<_>>(); + /// ``` + /// + fn dht_findpeer( + &self, + peer: &str, + ) -> Box<dyn Stream<Item = Result<response::DhtFindPeerResponse, Self::Error>> + Unpin> { + impl_stream_api_response! { + (self, request::DhtFindPeer { peer }, None) => request_stream_json + } + } + + /// Find peers in the DHT that can provide a specific value given a key. + /// + /// ```no_run + /// use futures::TryStreamExt; + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let key = "QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"; + /// let res = client.dht_findprovs(key).try_collect::<Vec<_>>(); + /// ``` + /// + fn dht_findprovs( + &self, + key: &str, + ) -> Box<dyn Stream<Item = Result<response::DhtFindProvsResponse, Self::Error>> + Unpin> { + impl_stream_api_response! { + (self, request::DhtFindProvs { key }, None) => request_stream_json + } + } + + /// Query the DHT for a given key. + /// + /// ```no_run + /// use futures::TryStreamExt; + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let key = "QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"; + /// let res = client.dht_get(key).try_collect::<Vec<_>>(); + /// ``` + /// + fn dht_get( + &self, + key: &str, + ) -> Box<dyn Stream<Item = Result<response::DhtGetResponse, Self::Error>> + Unpin> { + impl_stream_api_response! { + (self, request::DhtGet { key }, None) => request_stream_json + } + } + + /// Announce to the network that you are providing a given value. + /// + /// ```no_run + /// use futures::TryStreamExt; + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let key = "QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"; + /// let res = client.dht_provide(key).try_collect::<Vec<_>>(); + /// ``` + /// + fn dht_provide( + &self, + key: &str, + ) -> Box<dyn Stream<Item = Result<response::DhtProvideResponse, Self::Error>> + Unpin> { + impl_stream_api_response! { + (self, request::DhtProvide { key }, None) => request_stream_json + } + } + + /// Write a key/value pair to the DHT. + /// + /// ```no_run + /// use futures::TryStreamExt; + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.dht_put("test", "Hello World!").try_collect::<Vec<_>>(); + /// ``` + /// + fn dht_put( + &self, + key: &str, + value: &str, + ) -> Box<dyn Stream<Item = Result<response::DhtPutResponse, Self::Error>> + Unpin> { + impl_stream_api_response! { + (self, request::DhtPut { key, value }, None) => request_stream_json + } + } + + /// Find the closest peer given the peer ID by querying the DHT. + /// + /// ```no_run + /// use futures::TryStreamExt; + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let peer = "QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM"; + /// let res = client.dht_query(peer).try_collect::<Vec<_>>(); + /// ``` + /// + fn dht_query( + &self, + peer: &str, + ) -> Box<dyn Stream<Item = Result<response::DhtQueryResponse, Self::Error>> + Unpin> { + impl_stream_api_response! { + (self, request::DhtQuery { peer }, None) => request_stream_json + } + } + + /// Clear inactive requests from the log. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.diag_cmds_clear(); + /// ``` + /// + async fn diag_cmds_clear(&self) -> Result<response::DiagCmdsClearResponse, Self::Error> { + self.request_empty(request::DiagCmdsClear, None).await + } + + /// Set how long to keep inactive requests in the log. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.diag_cmds_set_time("1m"); + /// ``` + /// + async fn diag_cmds_set_time( + &self, + time: &str, + ) -> Result<response::DiagCmdsSetTimeResponse, Self::Error> { + self.request_empty(request::DiagCmdsSetTime { time }, None) + .await + } + + /// Print system diagnostic information. + /// + /// Note: There isn't good documentation on what this call is supposed to return. + /// It might be platform dependent, but if it isn't, this can be fixed to return + /// an actual object. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.diag_sys(); + /// ``` + /// + async fn diag_sys(&self) -> Result<response::DiagSysResponse, Self::Error> { + self.request_string(request::DiagSys, None).await + } + + /// Resolve DNS link. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.dns("ipfs.io", true); + /// ``` + /// + async fn dns(&self, link: &str, recursive: bool) -> Result<response::DnsResponse, Self::Error> { + self.request(request::Dns { link, recursive }, None).await + } + + /// List directory for Unix filesystem objects. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.file_ls("/ipns/ipfs.io"); + /// ``` + /// + async fn file_ls(&self, path: &str) -> Result<response::FileLsResponse, Self::Error> { + self.request(request::FileLs { path }, None).await + } + + /// Copy files into MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.files_cp("/path/to/file", "/dest"); + /// ``` + /// + async fn files_cp( + &self, + path: &str, + dest: &str, + ) -> Result<response::FilesCpResponse, Self::Error> { + self.files_cp_with_options(request::FilesCp { + path, + dest, + ..Default::default() + }) + .await + } + + /// Copy files into MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.files_cp("/path/to/file", "/dest"); + /// ``` + /// + async fn files_cp_with_options( + &self, + options: request::FilesCp<'_>, + ) -> Result<response::FilesCpResponse, Self::Error> { + self.request_empty(options, None).await + } + + /// Flush a path's data to disk. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.files_flush(None); + /// let res = client.files_flush(Some("/tmp")); + /// ``` + /// + async fn files_flush( + &self, + path: Option<&str>, + ) -> Result<response::FilesFlushResponse, Self::Error> { + self.request_empty(request::FilesFlush { path }, None).await + } + + /// List directories in MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.files_ls(None); + /// let res = client.files_ls(Some("/tmp")); + /// ``` + /// + async fn files_ls(&self, path: Option<&str>) -> Result<response::FilesLsResponse, Self::Error> { + self.files_ls_with_options(request::FilesLs { + path, + ..Default::default() + }) + .await + } + + /// List directories in MFS.. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// #[cfg(feature = "with-builder")] + /// let req = ipfs_api::request::FilesLs::builder() + /// // .path("/") // defaults to / + /// .unsorted(false) + /// .long(true) + /// .build(); + /// #[cfg(not(feature = "with-builder"))] + /// let req = ipfs_api::request::FilesLs { + /// path: None, // defaults to / + /// unsorted: Some(false), + /// long: Some(true), + /// }; + /// let res = client.files_ls_with_options(req); + /// ``` + /// + /// Defaults to `-U`, so the output is unsorted. + /// + async fn files_ls_with_options( + &self, + options: request::FilesLs<'_>, + ) -> Result<response::FilesLsResponse, Self::Error> { + self.request(options, None).await + } + + /// Make directories in MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.files_mkdir("/test", false); + /// let res = client.files_mkdir("/test/nested/dir", true); + /// ``` + /// + async fn files_mkdir( + &self, + path: &str, + parents: bool, + ) -> Result<response::FilesMkdirResponse, Self::Error> { + self.files_mkdir_with_options(request::FilesMkdir { + path, + parents: Some(parents), + ..Default::default() + }) + .await + } + + /// Make directories in MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// #[cfg(feature = "with-builder")] + /// let req = ipfs_api::request::FilesMkdir::builder() + /// .path("/test/nested/dir") + /// .parents(true) + /// .flush(false) + /// .build(); + /// #[cfg(not(feature = "with-builder"))] + /// let req = ipfs_api::request::FilesMkdir { + /// path: "/test/nested/dir", + /// parents: Some(true), + /// flush: Some(false), + /// .. Default::default() + /// }; + /// let res = client.files_mkdir_with_options(req); + /// ``` + /// + async fn files_mkdir_with_options( + &self, + options: request::FilesMkdir<'_>, + ) -> Result<response::FilesMkdirResponse, Self::Error> { + self.request_empty(options, None).await + } + + /// Copy files into MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.files_mv("/test/tmp.json", "/test/file.json"); + /// ``` + /// + async fn files_mv( + &self, + path: &str, + dest: &str, + ) -> Result<response::FilesMvResponse, Self::Error> { + self.files_mv_with_options(request::FilesMv { + path, + dest, + ..Default::default() + }) + .await + } + + /// Copy files into MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.files_mv_with_options( + /// ipfs_api::request::FilesMv { + /// path: "/test/tmp.json", + /// dest: "/test/file.json", + /// flush: Some(false), + /// } + /// ); + /// ``` + /// + async fn files_mv_with_options( + &self, + options: request::FilesMv<'_>, + ) -> Result<response::FilesMvResponse, Self::Error> { + self.request_empty(options, None).await + } + + /// Read a file in MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.files_read("/test/file.json"); + /// ``` + /// + fn files_read(&self, path: &str) -> Box<dyn Stream<Item = Result<Bytes, Self::Error>> + Unpin> { + self.files_read_with_options(request::FilesRead { + path, + ..request::FilesRead::default() + }) + } + + /// Read a file in MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// #[cfg(feature = "with-builder")] + /// let req = ipfs_api::request::FilesRead::builder() + /// .path("/test/file.json") + /// .offset(1024) + /// .count(8) + /// .build(); + /// #[cfg(not(feature = "with-builder"))] + /// let req = ipfs_api::request::FilesRead { + /// path: "/test/file.json", + /// offset: Some(1024), + /// count: Some(8), + /// }; + /// let res = client.files_read_with_options(req); + /// ``` + /// + fn files_read_with_options( + &self, + options: request::FilesRead, + ) -> Box<dyn Stream<Item = Result<Bytes, Self::Error>> + Unpin> { + impl_stream_api_response! { (self, options, None) => request_stream_bytes } + } + + /// Remove a file in MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// let res = client.files_rm("/test/dir", true); + /// let res = client.files_rm("/test/file.json", false); + /// ``` + /// + async fn files_rm( + &self, + path: &str, + recursive: bool, + ) -> Result<response::FilesRmResponse, Self::Error> { + self.files_rm_with_options(request::FilesRm { + path, + recursive: Some(recursive), + ..Default::default() + }) + .await + } + + /// Remove a file in MFS. + /// + /// ```no_run + /// use ipfs_api::{IpfsApi, IpfsClient}; + /// + /// let client = IpfsClient::default(); + /// #[cfg(feature = "with-builder")] + /// let req = ipfs_api::request::FilesRm::builder() + /// .path("/test/somefile.json") + /// .recursive(false) + /// .flush(false) + /// .build(); + /// #[cfg(not(feature = "with-builder"))] + /// let req = ipfs_api::request::FilesRm { + /// path: "/test/somefile.json", + /// recursive: Some(false), + /// flush: Some(false), + /// }; |