diff options
author | Julius Michaelis <gitter@liftm.de> | 2020-07-11 20:29:36 +0900 |
---|---|---|
committer | Julius Michaelis <gitter@liftm.de> | 2020-07-12 22:17:01 +0900 |
commit | 3905626f8f1c53a5ef933cd9bbefec31f8eb9355 (patch) | |
tree | 855c5c1f303df35001237fc53824f09f4efb2c8d | |
parent | b84a4be646ce2c781b44954989cc02c747794916 (diff) |
add builders for ipfs files {write, read, ls, rm, mkdir, chcid}
-rw-r--r-- | ipfs-api/examples/mfs.rs | 8 | ||||
-rw-r--r-- | ipfs-api/src/client/internal.rs | 271 | ||||
-rw-r--r-- | ipfs-api/src/request/files.rs | 90 |
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> { |