diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | .travis.yml | 5 | ||||
-rw-r--r-- | README.md | 66 | ||||
-rw-r--r-- | README.tpl | 20 | ||||
-rw-r--r-- | ipfs-api/Cargo.toml | 14 | ||||
-rw-r--r-- | ipfs-api/examples/get_version.rs | 26 | ||||
-rw-r--r-- | ipfs-api/examples/pubsub.rs | 2 | ||||
-rw-r--r-- | ipfs-api/src/client.rs | 1278 | ||||
-rw-r--r-- | ipfs-api/src/header.rs | 11 | ||||
-rw-r--r-- | ipfs-api/src/lib.rs | 67 | ||||
-rw-r--r-- | ipfs-api/src/request/pin.rs | 6 | ||||
-rw-r--r-- | ipfs-api/src/request/pubsub.rs | 6 | ||||
-rw-r--r-- | ipfs-api/src/response/mod.rs | 2 |
13 files changed, 1325 insertions, 179 deletions
@@ -1,3 +1,4 @@ /target/ +**/target/ **/*.rs.bk Cargo.lock diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b4f863c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: rust +rust: + - stable + - beta + - nightly @@ -1,23 +1,65 @@ -## Rust IPFS API Library +# ipfs-api + +[![Travis](https://img.shields.io/travis/ferristseng/rust-ipfs-api.svg)](https://travis-ci.org/ferristseng/rust-ipfs-api) +[![Crates.io](https://img.shields.io/crates/v/ipfs-api.svg)](https://crates.io/crates/ipfs-api) +[![Docs.rs](https://docs.rs/ipfs-api/badge.svg)](https://docs.rs/ipfs-api/) + +Rust library for connecting to the IPFS HTTP API using tokio. + +### Usage ```toml [dependencies] -ipfs-api = "0.4.0" +ipfs-api = "0.4.0-alpha" ``` -### Goals +### Examples + +Write a file to IPFS: - * Provide a full implementation of the HTTP API specification described here: https://ipfs.io/docs/api/. - * Write idiomatic rust, and make use of rust's memory safety features. - * Provide support for `go-ipfs 0.4.*`, with possible backwards compatibility features. - * Feature parity with the `go-ipfs` cli. - * Provide cross platform support for Linux, OSX, and Windows. +```rust +# +use ipfs_api::IpfsClient; +use std::io::Cursor; +use tokio_core::reactor::Core; + +let mut core = Core::new().unwrap(); +let client = IpfsClient::default(&core.handle()); +let data = Cursor::new("Hello World!"); + +let req = client.add(data); +let res = core.run(req).unwrap(); + +println!("{}", res.hash); +``` -#### Maybe (?) +Read a file from IPFS: - * Add integration tests for the `go-ipfs` implementation, and `js-ipfs` implementation of the ipfs spec. - * Explore a higher level API for interacting with IPFS. - * File system abstraction +```rust +# +use futures::stream::Stream; +use ipfs_api::IpfsClient; +use std::io::{self, Write}; +use tokio_core::reactor::Core; + +let mut core = Core::new().unwrap(); +let client = IpfsClient::default(&core.handle()); + +let req = client.get("/test/file.json").concat2(); +let res = core.run(req).unwrap(); +let out = io::stdout(); +let mut out = out.lock(); + +out.write_all(&res).unwrap(); +``` +There are also a bunch of examples included in the project, which +I used for testing + +You can run any of the examples with cargo: + +```sh +$ cargo run -p ipfs-api --example add_file +``` ## License diff --git a/README.tpl b/README.tpl new file mode 100644 index 0000000..7242565 --- /dev/null +++ b/README.tpl @@ -0,0 +1,20 @@ +# {{crate}} + +[![Travis](https://img.shields.io/travis/ferristseng/rust-ipfs-api.svg)](https://travis-ci.org/ferristseng/rust-ipfs-api) +[![Crates.io](https://img.shields.io/crates/v/ipfs-api.svg)](https://crates.io/crates/ipfs-api) +[![Docs.rs](https://docs.rs/ipfs-api/badge.svg)](https://docs.rs/ipfs-api/) + +{{readme}} + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/ipfs-api/Cargo.toml b/ipfs-api/Cargo.toml index 4670e40..798a598 100644 --- a/ipfs-api/Cargo.toml +++ b/ipfs-api/Cargo.toml @@ -1,15 +1,23 @@ [package] name = "ipfs-api" -version = "0.4.0" -license = "MIT OR Apache-2.0" +description = "Implementation of an IPFS HTTP API client" authors = ["Ferris Tseng <ferristseng@fastmail.fm>"] +documentation = "https://docs.rs/ipfs-api" +keywords = ["ipfs"] +categories = ["filesystem", "web-programming"] +version = "0.4.0-alpha" +readme = "../README.md" +license = "MIT OR Apache-2.0" + +[badges] +travis-ci = { repository = "ferristseng/rust-ipfs-api" } [dependencies] bytes = "0.4" error-chain = "0.11" futures = "0.1" hyper = "0.11" -hyper-multipart-rfc7578 = { git = "https://github.com/ferristseng/rust-hyper-multipart-rfc7578" } +hyper-multipart-rfc7578 = "0.1.0-alpha" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/ipfs-api/examples/get_version.rs b/ipfs-api/examples/get_version.rs index 257d1c7..deea4e6 100644 --- a/ipfs-api/examples/get_version.rs +++ b/ipfs-api/examples/get_version.rs @@ -6,11 +6,9 @@ // copied, modified, or distributed except according to those terms. // -extern crate futures; extern crate ipfs_api; extern crate tokio_core; -use futures::stream::Stream; use ipfs_api::IpfsClient; use tokio_core::reactor::Core; @@ -25,28 +23,4 @@ fn main() { let version = core.run(req).expect("expected a valid response"); println!("version: {:?}", version.version); - - let req = client.refs_local(); - println!( - "{:?}", - core.run(req.for_each(|res| Ok(println!("{:?}", res)))) - ); - - let req = client.diag_sys(); - println!("{}", core.run(req).expect("response")); - - let req = client.dns("ipfs.io", false); - let dns = core.run(req).expect("response"); - println!("{:?}", dns); - - let req = client.file_ls(&dns.path[..]); - println!("{:?}", core.run(req).expect("response")); - - /* - let req = client.dht_get("QmRJijhiMxQgn7bFP4cBsarHsGMM8g9fLDEE3WtkTXr4Hr"); - println!( - "{:?}", - core.run(req.for_each(|res| Ok(println!("{:?}", res)))) - ); - */ } diff --git a/ipfs-api/examples/pubsub.rs b/ipfs-api/examples/pubsub.rs index ed01b0b..59f8ce7 100644 --- a/ipfs-api/examples/pubsub.rs +++ b/ipfs-api/examples/pubsub.rs @@ -60,7 +60,7 @@ fn main() { { let mut event_loop = Core::new().expect("expected event loop"); let client = get_client(&event_loop.handle()); - let req = client.pubsub_sub(TOPIC, &None); + let req = client.pubsub_sub(TOPIC, false); println!(""); println!("waiting for messages on ({})...", TOPIC); diff --git a/ipfs-api/src/client.rs b/ipfs-api/src/client.rs index 387d48d..c5fb1bd 100644 --- a/ipfs-api/src/client.rs +++ b/ipfs-api/src/client.rs @@ -6,8 +6,8 @@ // copied, modified, or distributed except according to those terms. // -use futures::Stream; use futures::future::{Future, IntoFuture}; +use futures::stream::{self, Stream}; use header::Trailer; use read::{JsonLineDecoder, LineDecoder, StreamReader}; use request::{self, ApiRequest}; @@ -22,7 +22,7 @@ use tokio_core::reactor::Handle; use tokio_io::codec::{Decoder, FramedRead}; -/// A future response returned by the reqwest HTTP client. +/// A response returned by the HTTP client. /// type AsyncResponse<T> = Box<Future<Item = T, Error = Error>>; @@ -145,58 +145,77 @@ impl IpfsClient { Box::new(stream) } - /// Sends a request and returns the raw response. - /// - /// Methods prefixed with `send_` work on a raw reqwest `RequestBuilder` - /// instance. - /// - fn send_request(&self, req: Request<multipart::Body>) -> AsyncResponse<(StatusCode, Chunk)> { - let res = self.client - .request(req) - .and_then(|res| { - let status = res.status(); - - res.body().concat2().map(move |chunk| (status, chunk)) - }) - .from_err(); - - Box::new(res) - } - - /// Sends a request and deserializes the response into Json. - /// - /// Methods prefixed with `send_` work on a raw reqwest `RequestBuilder` - /// instance. + /// Generates a request, and returns the unprocessed response future. /// - fn send_request_json<Res>(&self, req: Request<multipart::Body>) -> AsyncResponse<Res> + fn request_raw<Req>( + &self, + req: &Req, + form: Option<multipart::Form>, + ) -> AsyncResponse<(StatusCode, Chunk)> where - for<'de> Res: 'static + Deserialize<'de>, + Req: ApiRequest + Serialize, { - let res = self.send_request(req).into_future().and_then( - |(status, chunk)| { - IpfsClient::process_json_response(status, chunk) - }, - ); - - Box::new(res) + match self.build_base_request(req, form) { + Ok(req) => { + let res = self.client + .request(req) + .and_then(|res| { + let status = res.status(); + + res.body().concat2().map(move |chunk| (status, chunk)) + }) + .from_err(); + + Box::new(res) + } + Err(e) => Box::new(Err(e).into_future()), + } } - /// Generates a request, and returns the unprocessed response future. + /// Generic method for making a request that expects back a streaming + /// response. /// - fn request_raw<Req>( + fn request_stream<Req, Res, F>( &self, req: &Req, form: Option<multipart::Form>, - ) -> AsyncResponse<(StatusCode, Chunk)> + process: F, + ) -> AsyncStreamResponse<Res> where Req: ApiRequest + Serialize, + Res: 'static, + F: 'static + Fn(hyper::Response) -> AsyncStreamResponse<Res>, { - let res = self.build_base_request(req, form) - .map(|req| self.send_request(req)) - .into_future() - .flatten(); + match self.build_base_request(req, form) { + Ok(req) => { + let res = self.client + .request(req) + .from_err() + .map(move |res| { + let stream: Box<Stream<Item = Res, Error = _>> = match res.status() { + StatusCode::Ok => process(res), + + // If the server responded with an error status code, the body + // still needs to be read so an error can be built. This block will + // read the entire body stream, then immediately return an error. + // + _ => Box::new( + res.body() + .concat2() + .from_err() + .and_then(|chunk| Err(Self::build_error_from_body(chunk))) + .into_stream(), + ), + }; - Box::new(res) + stream + }) + .flatten_stream(); + + Box::new(res) + } + Err(e) => Box::new(stream::once(Err(e))), + } } /// Generic method for making a request to the Ipfs server, and getting @@ -207,10 +226,9 @@ impl IpfsClient { Req: ApiRequest + Serialize, for<'de> Res: 'static + Deserialize<'de>, { - let res = self.build_base_request(req, form) - .map(|req| self.send_request_json(req)) - .into_future() - .flatten(); + let res = self.request_raw(req, form).and_then(|(status, chunk)| { + IpfsClient::process_json_response(status, chunk) + }); Box::new(res) } @@ -249,6 +267,7 @@ impl IpfsClient { Box::new(res) } + /// Generic method for making a request to the Ipfs server, and getting /// back a raw stream of bytes. /// @@ -260,41 +279,13 @@ impl IpfsClient { where Req: ApiRequest + Serialize, { - let res = self.build_base_request(req, form) - .map(|req| self.client.request(req).from_err()) - .into_future() - .flatten() - .map(|res| { - let stream: Box<Stream<Item = Chunk, Error = _>> = match res.status() { - // If the server responded OK, the data can be streamed back. - // - StatusCode::Ok => Box::new(res.body().map(|chunk| chunk).from_err()), - - // If the server responded with an error status code, the body - // still needs to be read so an error can be built. This block will - // read the entire body stream, then immediately return an error. - // - _ => Box::new( - res.body() - .concat2() - .from_err() - .and_then(|chunk| Err(Self::build_error_from_body(chunk))) - .into_stream(), - ), - - }; - - stream - }) - .flatten_stream(); - - Box::new(res) + self.request_stream(req, form, |res| Box::new(res.body().from_err())) } /// Generic method to return a streaming response of deserialized json /// objects delineated by new line separators. /// - fn request_stream<Req, Res>( + fn request_stream_json<Req, Res>( &self, req: &Req, form: Option<multipart::Form>, @@ -303,50 +294,48 @@ impl IpfsClient { Req: ApiRequest + Serialize, for<'de> Res: 'static + Deserialize<'de>, { - let res = self.build_base_request(req, form) - .map(|req| self.client.request(req).from_err()) - .into_future() - .flatten() - .map(|res| { - let stream: Box<Stream<Item = Res, Error = _>> = match res.status() { - StatusCode::Ok => { - let parse_stream_error = if let Some(trailer) = res.headers().get() { - // Response has the Trailer header set. The StreamError trailer - // is used to indicate that there was an error while streaming - // data with Ipfs. - // - match trailer { - &Trailer::StreamError => true, - } - } else { - false - }; - - Box::new(IpfsClient::process_stream_response( - res, - JsonLineDecoder::new(parse_stream_error), - )) - } - _ => Box::new( - res.body() - .concat2() - .from_err() - .and_then(|chunk| Err(Self::build_error_from_body(chunk))) - .into_stream(), - ), - }; - - stream - }) - .flatten_stream(); - - Box::new(res) + self.request_stream(req, form, |res| { + let parse_stream_error = if let Some(trailer) = res.headers().get() { + // Response has the Trailer header set. The StreamError trailer + // is used to indicate that there was an error while streaming + // data with Ipfs. + // + match trailer { + &Trailer::StreamError => true, + } + } else { + false + }; + + Box::new(IpfsClient::process_stream_response( + res, + JsonLineDecoder::new(parse_stream_error), + )) + }) } } impl IpfsClient { /// Add file to Ipfs. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use std::io::Cursor; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let data = Cursor::new("Hello World!"); + /// let req = client.add(data); + /// # } + /// ``` + /// #[inline] pub fn add<R>(&self, data: R) -> AsyncResponse<response::AddResponse> where @@ -361,6 +350,22 @@ impl IpfsClient { /// Returns the current ledger for a peer. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.bitswap_ledger("QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"); + /// # } + /// ``` + /// #[inline] pub fn bitswap_ledger(&self, peer: &str) -> AsyncResponse<response::BitswapLedgerResponse> { self.request(&request::BitswapLedger { peer }, None) @@ -368,6 +373,22 @@ impl IpfsClient { /// Returns some stats about the bitswap agent. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.bitswap_stat(); + /// # } + /// ``` + /// #[inline] pub fn bitswap_stat(&self) -> AsyncResponse<response::BitswapStatResponse> { self.request(&request::BitswapStat, None) @@ -375,6 +396,22 @@ impl IpfsClient { /// Remove a given block from your wantlist. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.bitswap_unwant("QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"); + /// # } + /// ``` + /// #[inline] pub fn bitswap_unwant(&self, key: &str) -> AsyncResponse<response::BitswapUnwantResponse> { self.request_empty(&request::BitswapUnwant { key }, None) @@ -382,6 +419,22 @@ impl IpfsClient { /// Shows blocks on the wantlist for you or the specified peer. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.bitswap_wantlist(Some("QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ")); + /// # } + /// ``` + /// #[inline] pub fn bitswap_wantlist( &self, @@ -392,6 +445,25 @@ impl IpfsClient { /// Gets a raw IPFS block. /// + /// # Examples + /// + /// ```no_run + /// # extern crate futures; + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use futures::stream::Stream; + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let hash = "QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"; + /// let req = client.block_get(hash).concat2(); + /// # } + /// ``` + /// #[inline] pub fn block_get(&self, hash: &str) -> AsyncStreamResponse<Chunk> { self.request_stream_bytes(&request::BlockGet { hash }, None) @@ -399,6 +471,24 @@ impl IpfsClient { /// Store input as an IPFS block. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use std::io::Cursor; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let data = Cursor::new("Hello World!"); + /// let req = client.block_put(data); + /// # } + /// ``` + /// #[inline] pub fn block_put<R>(&self, data: R) -> AsyncResponse<response::BlockPutResponse> where @@ -413,6 +503,22 @@ impl IpfsClient { /// Removes an IPFS block. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.block_rm("QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"); + /// # } + /// ``` + /// #[inline] pub fn block_rm(&self, hash: &str) -> AsyncResponse<response::BlockRmResponse> { self.request(&request::BlockRm { hash }, None) @@ -420,6 +526,22 @@ impl IpfsClient { /// Prints information about a raw IPFS block. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.block_stat("QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"); + /// # } + /// ``` + /// #[inline] pub fn block_stat(&self, hash: &str) -> AsyncResponse<response::BlockStatResponse> { self.request(&request::BlockStat { hash }, None) @@ -427,6 +549,22 @@ impl IpfsClient { /// Add default peers to the bootstrap list. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.bootstrap_add_default(); + /// # } + /// ``` + /// #[inline] pub fn bootstrap_add_default(&self) -> AsyncResponse<response::BootstrapAddDefaultResponse> { self.request(&request::BootstrapAddDefault, None) @@ -434,6 +572,22 @@ impl IpfsClient { /// Lists peers in bootstrap list. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.bootstrap_list(); + /// # } + /// ``` + /// #[inline] pub fn bootstrap_list(&self) -> AsyncResponse<response::BootstrapListResponse> { self.request(&request::BootstrapList, None) @@ -441,6 +595,22 @@ impl IpfsClient { /// Removes all peers in bootstrap list. /// + /// # Examples + /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.bootstrap_rm_all(); + /// # } + /// ``` + /// #[inline] pub fn bootstrap_rm_all(&self) -> AsyncResponse<response::BootstrapRmAllResponse> { self.request(&request::BootstrapRmAll, None) @@ -448,6 +618,25 @@ impl IpfsClient { /// Returns the contents of an Ipfs object. /// + /// # Examples + /// + /// ```no_run + /// # extern crate futures; + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use futures::stream::Stream; + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let hash = "QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"; + /// let req = client.cat(hash).concat2(); + /// # } + /// ``` + /// #[inline] pub fn cat(&self, path: &str) -> AsyncStreamResponse<Chunk> { self.request_stream_bytes(&request::Cat { path }, None) @@ -455,6 +644,20 @@ impl IpfsClient { /// List available commands that the server accepts. /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.commands(); + /// # } + /// ``` + /// #[inline] pub fn commands(&self) -> AsyncResponse<response::CommandsResponse> { self.request(&request::Commands, None) @@ -462,6 +665,20 @@ impl IpfsClient { /// Opens the config file for editing (on the server). /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.config_edit(); + /// # } + /// ``` + /// #[inline] pub fn config_edit(&self) -> AsyncResponse<response::ConfigEditResponse> { self.request(&request::ConfigEdit, None) @@ -469,6 +686,22 @@ impl IpfsClient { /// Replace the config file. /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use std::io::Cursor; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let config = Cursor::new("{..json..}"); + /// let req = client.config_replace(config); + /// # } + /// ``` + /// #[inline] pub fn config_replace<R>(&self, data: R) -> AsyncResponse<response::ConfigReplaceResponse> where @@ -485,6 +718,20 @@ impl IpfsClient { /// /// Returns an unparsed json string, due to an unclear spec. /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.config_show(); + /// # } + /// ``` + /// #[inline] pub fn config_show(&self) -> AsyncResponse<response::ConfigShowResponse> { self.request_string(&request::ConfigShow, None) @@ -492,6 +739,20 @@ impl IpfsClient { /// Returns information about a dag node in Ipfs. /// + /// ```no_run + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let req = client.dag_get("QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"); + /// # } + /// ``` + /// #[inline] pub fn dag_get(&self, path: &str) -> AsyncResponse<response::DagGetResponse> { self.request(&request::DagGet { path }, None) @@ -516,48 +777,163 @@ impl IpfsClient { /// Query the DHT for all of the multiaddresses associated with a Peer ID. /// + /// ```no_run + /// # extern crate futures; + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use futures::stream::Stream; + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let peer = "QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM"; + /// let req = client.dht_findpeer(peer).collect(); + /// # } + /// ``` + /// #[inline] pub fn dht_findpeer(&self, peer: &str) -> AsyncStreamResponse<response::DhtFindPeerResponse> { - self.request_stream(&request::DhtFindPeer { peer }, None) + self.request_stream_json(&request::DhtFindPeer { peer }, None) } /// Find peers in the DHT that can provide a specific value given a key. /// + /// ```no_run + /// # extern crate futures; + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use futures::stream::Stream; + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new().unwrap(); + /// let client = IpfsClient::default(&core.handle()); + /// let key = "QmXdNSQx7nbdRvkjGCEQgVjVtVwsHvV8NmV2a8xzQVwuFA"; + /// let req = client.dht_findprovs(key).collect(); + /// # } + /// ``` + /// #[inline] pub fn dht_findprovs(&self, key: &str) -> AsyncStreamResponse<response::DhtFindProvsResponse> { - self.request_stream(&request::DhtFindProvs { key }, None) + self.request_stream_json(&request::DhtFindProvs { key }, None) } /// Query the DHT for a given key. /// + /// ```no_run + /// # extern crate futures; + /// # extern crate ipfs_api; + /// # extern crate tokio_core; + /// # + /// use futures::stream::Stream; + /// use ipfs_api::IpfsClient; + /// use tokio_core::reactor::Core; + /// + /// # fn main() { + /// let mut core = Core::new( |