summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulius Michaelis <gitter@liftm.de>2020-07-11 20:29:36 +0900
committerJulius Michaelis <gitter@liftm.de>2020-07-12 22:17:01 +0900
commit3905626f8f1c53a5ef933cd9bbefec31f8eb9355 (patch)
tree855c5c1f303df35001237fc53824f09f4efb2c8d
parentb84a4be646ce2c781b44954989cc02c747794916 (diff)
add builders for ipfs files {write, read, ls, rm, mkdir, chcid}
-rw-r--r--ipfs-api/examples/mfs.rs8
-rw-r--r--ipfs-api/src/client/internal.rs271
-rw-r--r--ipfs-api/src/request/files.rs90
3 files changed, 274 insertions, 95 deletions
diff --git a/ipfs-api/examples/mfs.rs b/ipfs-api/examples/mfs.rs
index 2b20111..9deb90f 100644
--- a/ipfs-api/examples/mfs.rs
+++ b/ipfs-api/examples/mfs.rs
@@ -31,7 +31,7 @@ async fn main() {
eprintln!("making /test...");
eprintln!();
- if let Err(e) = client.files_mkdir("/test", false, 0, None, true).await {
+ if let Err(e) = client.files_mkdir("/test", false).await {
eprintln!("error making /test: {}", e);
return;
}
@@ -39,7 +39,7 @@ async fn main() {
eprintln!("making dirs /test/does/not/exist/yet...");
eprintln!();
- if let Err(e) = client.files_mkdir("/test/does/not/exist/yet", true, 0, None, true).await {
+ if let Err(e) = client.files_mkdir("/test/does/not/exist/yet", true).await {
eprintln!("error making /test/does/not/exist/yet: {}", e);
return;
}
@@ -60,7 +60,7 @@ async fn main() {
let src = File::open(file!()).expect("could not read source file");
- if let Err(e) = client.files_write("/test/mfs.rs", true, true, false, 0, None, false, 0, None, true, src).await {
+ if let Err(e) = client.files_write("/test/mfs.rs", src).await {
eprintln!("error writing source file /test/mfs.rs: {}", e);
return;
}
@@ -79,7 +79,7 @@ async fn main() {
eprintln!("removing /test...");
eprintln!();
- if let Err(e) = client.files_rm("/test", true, true).await {
+ if let Err(e) = client.files_rm("/test", true).await {
eprintln!("error removing /test: {}", e);
}
diff --git a/ipfs-api/src/client/internal.rs b/ipfs-api/src/client/internal.rs
index 885f6a4..420e896 100644
--- a/ipfs-api/src/client/internal.rs
+++ b/ipfs-api/src/client/internal.rs
@@ -39,6 +39,8 @@ use std::{
};
use tokio_util::codec::{Decoder, FramedRead};
+fn default<T: Default>() -> T { Default::default() }
+
const FILE_DESCRIPTOR_LIMIT: usize = 127;
#[cfg(feature = "actix")]
@@ -1145,19 +1147,50 @@ impl IpfsClient {
self.request_empty(request::FilesFlush { path }, None).await
}
- /// List directories in MFS. Always passes `-U`.
+ /// List directories in MFS..
///
/// ```no_run
/// use ipfs_api::IpfsClient;
///
/// let client = IpfsClient::default();
- /// let res = client.files_ls(None, false);
- /// let res = client.files_ls(Some("/tmp"), true);
+ /// let res = client.files_ls(None);
+ /// let res = client.files_ls(Some("/tmp"));
/// ```
///
+ /// Defaults to `-U`, so the output is unsorted.
+ ///
#[inline]
- pub async fn files_ls(&self, path: Option<&str>, long: bool) -> Result<response::FilesLsResponse, Error> {
- self.request(request::FilesLs { path, long, unsorted: true }, None).await
+ pub async fn files_ls(&self, path: Option<&str>) -> Result<response::FilesLsResponse, Error> {
+ self.files_ls_with_options(request::FilesLs { path: path, .. default() }).await
+ }
+
+ /// List directories in MFS..
+ ///
+ /// ```no_run
+ /// let client = ipfs_api::IpfsClient::default();
+ /// #[cfg(feature = "builder")]
+ /// let req = ipfs_api::request::FilesLs::builder()
+ /// // .path("/") // defaults to /
+ /// .unsorted(false)
+ /// .long(true)
+ /// .build();
+ /// #[cfg(not(feature = "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.
+ ///
+ #[inline]
+ pub async fn files_ls_with_options(
+ &self,
+ options: request::FilesLs<'_>
+ ) -> Result<response::FilesLsResponse, Error> {
+ self.request(options, None).await
}
/// Make directories in MFS.
@@ -1166,20 +1199,43 @@ impl IpfsClient {
/// use ipfs_api::IpfsClient;
///
/// let client = IpfsClient::default();
- /// let res = client.files_mkdir("/test", false, 0, None, true);
- /// let res = client.files_mkdir("/test/nested/dir", true, 0, None, true);
+ /// let res = client.files_mkdir("/test", false);
+ /// let res = client.files_mkdir("/test/nested/dir", true);
+ /// ```
+ ///
+ #[inline]
+ pub async fn files_mkdir(&self, path: &str, parents: bool) -> Result<response::FilesMkdirResponse, Error> {
+ self.files_mkdir_with_options(request::FilesMkdir { path: path, parents: Some(parents), .. default() }).await
+ }
+
+ /// Make directories in MFS.
+ ///
+ /// ```no_run
+ /// use ipfs_api::IpfsClient;
+ ///
+ /// let client = IpfsClient::default();
+ /// #[cfg(feature = "builder")]
+ /// let req = ipfs_api::request::FilesMkdir::builder()
+ /// .path("/test/nested/dir")
+ /// .parents(true)
+ /// .flush(false)
+ /// .build();
+ /// #[cfg(not(feature = "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);
/// ```
///
#[inline]
- pub async fn files_mkdir(
+ pub async fn files_mkdir_with_options(
&self,
- path: &str,
- parents: bool,
- cid_version: i32,
- hash: Option<&str>,
- flush: bool,
+ options: request::FilesMkdir<'_>
) -> Result<response::FilesMkdirResponse, Error> {
- self.request_empty(request::FilesMkdir { path, parents, cid_version, hash, flush }, None)
+ self.request_empty(options, None)
.await
}
@@ -1209,16 +1265,38 @@ impl IpfsClient {
/// use ipfs_api::IpfsClient;
///
/// let client = IpfsClient::default();
- /// let res = client.files_read("/test/file.json", 0, None);
+ /// let res = client.files_read("/test/file.json");
/// ```
///
- /// Not specifying a byte `count` reads to the end of the file.
+ #[inline]
+ pub fn files_read(&self, path: &str) -> impl Stream<Item = Result<Bytes, Error>> {
+ self.files_read_with_options(request::FilesRead { path, .. request::FilesRead::default() })
+ }
+
+ /// Read a file in MFS.
+ ///
+ /// ```no_run
+ /// use ipfs_api::IpfsClient;
+ ///
+ /// let client = IpfsClient::default();
+ /// #[cfg(feature = "builder")]
+ /// let req = ipfs_api::request::FilesRead::builder()
+ /// .path("/test/file.json")
+ /// .offset(1024)
+ /// .count(8)
+ /// .build();
+ /// #[cfg(not(feature = "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);
+ /// ```
///
#[inline]
- pub fn files_read(&self, path: &str, offset: i64, count: Option<i64>) -> impl Stream<Item = Result<Bytes, Error>> {
- impl_stream_api_response! {
- (self, request::FilesRead { path, offset, count }, None) => request_stream_bytes
- }
+ pub fn files_read_with_options(&self, options: request::FilesRead) -> impl Stream<Item = Result<Bytes, Error>> {
+ impl_stream_api_response! { (self, options, None) => request_stream_bytes }
}
/// Remove a file in MFS.
@@ -1227,8 +1305,8 @@ impl IpfsClient {
/// use ipfs_api::IpfsClient;
///
/// let client = IpfsClient::default();
- /// let res = client.files_rm("/test/dir", true, true);
- /// let res = client.files_rm("/test/file.json", false, true);
+ /// let res = client.files_rm("/test/dir", true);
+ /// let res = client.files_rm("/test/file.json", false);
/// ```
///
#[inline]
@@ -1236,13 +1314,40 @@ impl IpfsClient {
&self,
path: &str,
recursive: bool,
- flush: bool,
) -> Result<response::FilesRmResponse, Error> {
- self.request_empty(request::FilesRm { path, recursive, flush }, None)
- .await
+ self.files_rm_with_options(request::FilesRm { path, recursive: Some(recursive), .. default() }).await
+ }
+
+ /// Remove a file in MFS.
+ ///
+ /// ```no_run
+ /// use ipfs_api::IpfsClient;
+ ///
+ /// let client = IpfsClient::default();
+ /// #[cfg(feature = "builder")]
+ /// let req = ipfs_api::request::FilesRm::builder()
+ /// .path("/test/somefile.json")
+ /// .recursive(false)
+ /// .flush(false)
+ /// .build();
+ /// #[cfg(not(feature = "builder"))]
+ /// let req = ipfs_api::request::FilesRm {
+ /// path: "/test/somefile.json",
+ /// recursive: Some(false),
+ /// flush: Some(false),
+ /// };
+ /// let res = client.files_rm_with_options(req);
+ /// ```
+ ///
+ #[inline]
+ pub async fn files_rm_with_options(
+ &self,
+ options: request::FilesRm<'_>
+ ) -> Result<response::FilesRmResponse, Error> {
+ self.request_empty(options, None).await
}
- /// Display a file's status in MDFS.
+ /// Display a file's status in MFS.
///
/// ```no_run
/// use ipfs_api::IpfsClient;
@@ -1264,24 +1369,53 @@ impl IpfsClient {
///
/// let client = IpfsClient::default();
/// let file = File::open("test.json").unwrap();
- /// let res = client.files_write("/test/file.json", true, true, true, 0, None, false, 0, None, true, file);
+ /// let res = client.files_write("/test/file.json", file);
/// ```
///
- /// Not specifying a byte `count` writes the entire input.
+ /// For convenience reasons, this defaults create and truncate to true
///
#[inline]
pub async fn files_write<R>(
&self,
path: &str,
- create: bool,
- truncate: bool,
- parents: bool,
- offset: i64,
- count: Option<i64>,
- raw_leaves: bool,
- cid_version: i32,
- hash: Option<&str>,
- flush: bool,
+ data: R,
+ ) -> Result<response::FilesWriteResponse, Error>
+ where
+ R: 'static + Read + Send + Sync,
+ {
+ self.files_write_with_options(request::FilesWrite { path, .. request::FilesWrite::default() }, data).await
+ }
+
+ /// Write to a mutable file in the filesystem.
+ ///
+ /// ```no_run
+ /// let client = ipfs_api::IpfsClient::default();
+ /// let data = std::io::Cursor::new((1..128).collect::<Vec<u8>>());
+ /// #[cfg(feature = "builder")]
+ /// let req = ipfs_api::request::FilesWrite::builder()
+ /// .path("/test/outfile.bin")
+ /// .create(false)
+ /// .truncate(false)
+ /// .offset(1 << 20)
+ /// .flush(false)
+ /// // see FilesWriteBuilder for the full set of options
+ /// .build();
+ /// #[cfg(not(feature = "builder"))]
+ /// let req = ipfs_api::request::FilesWrite {
+ /// path: "/test/outfile.bin",
+ /// create: Some(false),
+ /// truncate: Some(false),
+ /// offset: Some(1 << 20),
+ /// flush: Some(false),
+ /// .. Default::default()
+ /// };
+ /// let res = client.files_write_with_options(req, data);
+ /// ```
+ ///
+ #[inline]
+ pub async fn files_write_with_options<R>(
+ &self,
+ options: request::FilesWrite<'_>,
data: R,
) -> Result<response::FilesWriteResponse, Error>
where
@@ -1291,22 +1425,7 @@ impl IpfsClient {
form.add_reader("data", data);
- self.request_empty(
- request::FilesWrite {
- path,
- create,
- truncate,
- parents,
- offset,
- count,
- raw_leaves,
- cid_version,
- hash,
- flush,
- },
- Some(form),
- )
- .await
+ self.request_empty(options, Some(form)).await
}
/// Change the cid version or hash function of the root node of a given path.
@@ -1316,7 +1435,7 @@ impl IpfsClient {
/// use std::fs::File;
///
/// let client = IpfsClient::default();
- /// let res = client.files_chcid("/test/", 1, Some("sha3-512"), true);
+ /// let res = client.files_chcid("/test/", 1);
/// ```
///
/// Not specifying a byte `count` writes the entire input.
@@ -1326,12 +1445,48 @@ impl IpfsClient {
&self,
path: &str,
cid_version: i32,
- hash: Option<&str>,
- flush: bool,
) -> Result<response::FilesChcidResponse, Error>
{
- self.request_empty(request::FilesChcid { path, cid_version, hash, flush }, None)
- .await
+ self.request_empty(request::FilesChcid {
+ path: Some(path),
+ cid_version: Some(cid_version),
+ .. default()
+ }, None).await
+ }
+
+ /// Change the cid version or hash function of the root node of a given path.
+ ///
+ /// ```no_run
+ /// use ipfs_api::IpfsClient;
+ /// use std::fs::File;
+ ///
+ /// let client = IpfsClient::default();
+ /// #[cfg(feature = "builder")]
+ /// let req = ipfs_api::request::FilesChcid::builder()
+ /// .path("/test/")
+ /// .cid_version(1)
+ /// .hash("sha3-512")
+ /// .flush(true)
+ /// .build();
+ /// #[cfg(not(feature = "builder"))]
+ /// let req = ipfs_api::request::FilesChcid {
+ /// path: Some("/test/"),
+ /// cid_version: Some(1),
+ /// hash: Some("sha3-512"),
+ /// flush: Some(false),
+ /// };
+ /// let res = client.files_chcid_with_options(req);
+ /// ```
+ ///
+ /// Not specifying a byte `count` writes the entire input.
+ ///
+ #[inline]
+ pub async fn files_chcid_with_options(
+ &self,
+ options: request::FilesChcid<'_>
+ ) -> Result<response::FilesChcidResponse, Error>
+ {
+ self.request_empty(options, None).await
}
/// List blocks that are both in the filestore and standard block storage.
diff --git a/ipfs-api/src/request/files.rs b/ipfs-api/src/request/files.rs
index 109f107..2a7b996 100644
--- a/ipfs-api/src/request/files.rs
+++ b/ipfs-api/src/request/files.rs
@@ -34,35 +34,43 @@ impl<'a> ApiRequest for FilesFlush<'a> {
const PATH: &'static str = "/files/flush";
}
-#[derive(Serialize)]
+#[cfg_attr(feature = "builder", derive(TypedBuilder))]
+#[derive(Serialize, Default)]
pub struct FilesLs<'a> {
#[serde(rename = "arg")]
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
pub path: Option<&'a str>,
- pub long: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub long: Option<bool>,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
#[serde(rename = "U")]
- pub unsorted: bool,
+ pub unsorted: Option<bool>,
}
impl<'a> ApiRequest for FilesLs<'a> {
const PATH: &'static str = "/files/ls";
}
-#[derive(Serialize)]
+#[cfg_attr(feature = "builder", derive(TypedBuilder))]
+#[derive(Serialize, Default)]
+#[serde(rename_all = "kebab-case")]
pub struct FilesMkdir<'a> {
#[serde(rename = "arg")]
pub path: &'a str,
- pub parents: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub parents: Option<bool>,
- #[serde(skip_serializing_if = "Option::is_none")]
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
pub hash: Option<&'a str>,
- #[serde(rename = "cid-version")]
- pub cid_version: i32,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub cid_version: Option<i32>,
- pub flush: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub flush: Option<bool>,
}
impl<'a> ApiRequest for FilesMkdir<'a> {
@@ -84,14 +92,16 @@ impl<'a> ApiRequest for FilesMv<'a> {
const PATH: &'static str = "/files/mv";
}
-#[derive(Serialize)]
+#[cfg_attr(feature = "builder", derive(TypedBuilder))]
+#[derive(Serialize, Default)]
pub struct FilesRead<'a> {
#[serde(rename = "arg")]
pub path: &'a str,
- pub offset: i64,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub offset: Option<i64>,
- #[serde(skip_serializing_if = "Option::is_none")]
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
pub count: Option<i64>,
}
@@ -99,14 +109,17 @@ impl<'a> ApiRequest for FilesRead<'a> {
const PATH: &'static str = "/files/read";
}
-#[derive(Serialize)]
+#[cfg_attr(feature = "builder", derive(TypedBuilder))]
+#[derive(Serialize, Default)]
pub struct FilesRm<'a> {
#[serde(rename = "arg")]
pub path: &'a str,
- pub recursive: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub recursive: Option<bool>,
- pub flush: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub flush: Option<bool>,
}
impl<'a> ApiRequest for FilesRm<'a> {
@@ -126,50 +139,61 @@ impl<'a> ApiRequest for FilesStat<'a> {
const PATH: &'static str = "/files/stat";
}
-#[derive(Serialize)]
+#[cfg_attr(feature = "builder", derive(TypedBuilder))]
+#[derive(Serialize, Default)]
+#[serde(rename_all = "kebab-case")]
pub struct FilesWrite<'a> {
#[serde(rename = "arg")]
pub path: &'a str,
- pub create: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub create: Option<bool>,
- pub truncate: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub truncate: Option<bool>,
- pub parents: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub parents: Option<bool>,
- pub offset: i64,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub offset: Option<i64>,
- #[serde(skip_serializing_if = "Option::is_none")]
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
pub count: Option<i64>,
- #[serde(rename = "raw-leaves")]
- pub raw_leaves: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub raw_leaves: Option<bool>,
- #[serde(skip_serializing_if = "Option::is_none")]
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
pub hash: Option<&'a str>,
- #[serde(rename = "cid-version")]
- pub cid_version: i32,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub cid_version: Option<i32>,
- pub flush: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub flush: Option<bool>,
}
impl<'a> ApiRequest for FilesWrite<'a> {
const PATH: &'static str = "/files/write";
}
-#[derive(Serialize)]
+#[cfg_attr(feature = "builder", derive(TypedBuilder))]
+#[derive(Serialize, Default)]
+#[serde(rename_all = "kebab-case")]
pub struct FilesChcid<'a> {
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
#[serde(rename = "arg")]
- pub path: &'a str,
+ pub path: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
pub hash: Option<&'a str>,
- #[serde(rename = "cid-version")]
- pub cid_version: i32,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub cid_version: Option<i32>,
- pub flush: bool,
+ #[cfg_attr(feature = "builder", builder(default, setter(strip_option)))]
+ pub flush: Option<bool>,
}
impl<'a> ApiRequest for FilesChcid<'a> {