diff options
-rw-r--r-- | ipfs-api/Cargo.toml | 2 | ||||
-rw-r--r-- | ipfs-api/examples/add_file.rs | 27 | ||||
-rw-r--r-- | ipfs-api/examples/get_commands.rs | 3 | ||||
-rw-r--r-- | ipfs-api/examples/get_version.rs | 9 | ||||
-rw-r--r-- | ipfs-api/src/client.rs | 90 | ||||
-rw-r--r-- | ipfs-api/src/lib.rs | 2 | ||||
-rw-r--r-- | ipfs-api/src/request/add.rs | 11 | ||||
-rw-r--r-- | ipfs-api/src/request/mod.rs | 2 | ||||
-rw-r--r-- | ipfs-api/src/response/add.rs | 2 | ||||
-rw-r--r-- | ipfs-api/src/response/error.rs | 16 |
10 files changed, 119 insertions, 45 deletions
diff --git a/ipfs-api/Cargo.toml b/ipfs-api/Cargo.toml index 3e57393..9c5e4f0 100644 --- a/ipfs-api/Cargo.toml +++ b/ipfs-api/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Ferris Tseng <ferristseng@fastmail.fm>"] [dependencies] futures = "0.1" -hyper = "0.11" +reqwest = { version = "0.8", features = ["unstable"] } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/ipfs-api/examples/add_file.rs b/ipfs-api/examples/add_file.rs new file mode 100644 index 0000000..8ebbddf --- /dev/null +++ b/ipfs-api/examples/add_file.rs @@ -0,0 +1,27 @@ +extern crate ipfs_api; +extern crate tokio_core; + +use ipfs_api::{response, IpfsClient}; +use std::fs::File; +use tokio_core::reactor::Core; + + +// Creates an Ipfs client, and gets a list of available commands from the +// Ipfs server. +// +fn main() { + if let Ok(mut core) = Core::new() { + println!("note: this must be run in the root of the project repository"); + println!("connecting to localhost:5001..."); + + let client = + IpfsClient::new(&core.handle(), "localhost", 5001).expect("expected a valid url"); + let file = File::open(file!()).expect("could not read source file"); + let req = client.add(file).expect("expected a valid request"); + let add = core.run(req).expect("expected a valid response"); + + println!("added file: {:?}", add); + } else { + println!("failed to create event loop"); + } +} diff --git a/ipfs-api/examples/get_commands.rs b/ipfs-api/examples/get_commands.rs index 9d03f54..2ac6bbe 100644 --- a/ipfs-api/examples/get_commands.rs +++ b/ipfs-api/examples/get_commands.rs @@ -1,7 +1,6 @@ extern crate ipfs_api; extern crate tokio_core; - use ipfs_api::{response, IpfsClient}; use tokio_core::reactor::Core; @@ -37,7 +36,7 @@ fn main() { let client = IpfsClient::new(&core.handle(), "localhost", 5001).expect("expected a valid url"); - let req = client.commands(); + let req = client.commands().expect("expected a valid request"); print_recursive(0, &core.run(req).expect("expected a valid response")); } else { diff --git a/ipfs-api/examples/get_version.rs b/ipfs-api/examples/get_version.rs index 5e2c608..e605da3 100644 --- a/ipfs-api/examples/get_version.rs +++ b/ipfs-api/examples/get_version.rs @@ -1,7 +1,6 @@ extern crate ipfs_api; extern crate tokio_core; - use ipfs_api::IpfsClient; use tokio_core::reactor::Core; @@ -14,12 +13,10 @@ fn main() { let client = IpfsClient::new(&core.handle(), "localhost", 5001).expect("expected a valid url"); - let req = client.version(); + let req = client.version().expect("expected a valid request"); + let version = core.run(req).expect("expected a valid response"); - println!( - "version: {:?}", - core.run(req).expect("expected a valid response") - ) + println!("version: {:?}", version.version); } else { println!("failed to create event loop"); } diff --git a/ipfs-api/src/client.rs b/ipfs-api/src/client.rs index fe98b0f..ff3eb52 100644 --- a/ipfs-api/src/client.rs +++ b/ipfs-api/src/client.rs @@ -1,95 +1,125 @@ -use futures::Stream; use futures::future::Future; -use hyper::{Chunk, Uri}; -use hyper::client::{Client, HttpConnector}; -use hyper::error::UriError; use request::{self, ApiRequest}; +use reqwest::{self, multipart, Method, Url}; +use reqwest::unstable::async::{Client, ClientBuilder}; use response::{self, Error}; use serde::Deserialize; -use serde_json; +use std::io::Read; use tokio_core::reactor::Handle; -pub type AsyncResponse<T> = Box<Future<Item = T, Error = Error>>; +/// A future response returned by the reqwest HTTP client. +/// +type AsyncResponse<T> = Box<Future<Item = T, Error = Error>>; /// Asynchronous Ipfs client. /// pub struct IpfsClient { - base: Uri, - client: Client<HttpConnector>, + base: Url, + client: Client, } impl IpfsClient { /// Creates a new `IpfsClient`. /// #[inline] - pub fn new(handle: &Handle, host: &str, port: u16) -> Result<IpfsClient, UriError> { + pub fn new( + handle: &Handle, + host: &str, + port: u16, + ) -> Result<IpfsClient, Box<::std::error::Error>> { let base_path = IpfsClient::build_base_path(host, port)?; Ok(IpfsClient { base: base_path, - client: Client::new(handle), + client: ClientBuilder::new().build(handle)?, }) } - /// Builds the base uri path for the Ipfs API. + /// Builds the base url path for the Ipfs api. /// - fn build_base_path(host: &str, port: u16) -> Result<Uri, UriError> { + fn build_base_path(host: &str, port: u16) -> Result<Url, reqwest::UrlError> { format!("http://{}:{}/api/v0", host, port).parse() } - /// Generic method for making a request to the Ipfs server. + /// Builds the url for an api call. /// - /// Returns the raw response. + fn build_url<Req>(&self, req: &Req) -> Result<Url, reqwest::UrlError> + where + Req: ApiRequest, + { + let uri = format!("{}{}?", self.base, Req::path()).parse()?; + + Ok(uri) + } + + /// Generic method for making a request to the Ipfs server, and getting + /// a deserializable response. /// - fn request_raw<Req>(&self, req: &Req) -> AsyncResponse<Chunk> + fn request<Req, Res>(&self, req: &Req) -> Result<AsyncResponse<Res>, Error> where Req: ApiRequest, + for<'de> Res: 'static + Deserialize<'de>, { - let uri = format!("{}{}?", self.base, Req::path()).parse().unwrap(); - - Box::new( - self.client - .get(uri) - .and_then(|res| res.body().concat2()) - .from_err(), - ) + let url = self.build_url(req)?; + let mut req = self.client.request(Method::Get, url); + let res = req.send().and_then(move |mut res| res.json()).from_err(); + + Ok(Box::new(res)) } /// Generic method for making a request to the Ipfs server, and getting /// a deserializable response. /// - fn request<Req, Res>(&self, req: &Req) -> AsyncResponse<Res> + fn request_with_body<Req, Res, R>( + &self, + data: R, + req: &Req, + ) -> Result<AsyncResponse<Res>, Error> where Req: ApiRequest, for<'de> Res: 'static + Deserialize<'de>, + R: 'static + Read + Send, { - Box::new(self.request_raw(req).and_then(move |body| { - serde_json::from_slice(&body).map_err(From::from) - })) + let url = self.build_url(req)?; + let form = multipart::Form::new().part("file", multipart::Part::reader(data)); + let mut req = self.client.request(Method::Get, url); //.form(&form); + let res = req.send().and_then(move |mut res| res.json()).from_err(); + + Ok(Box::new(res)) } } impl IpfsClient { + /// Add file to Ipfs. + /// + #[inline] + pub fn add<R>(&self, data: R) -> Result<AsyncResponse<response::AddResponse>, Error> + where + R: 'static + Read + Send, + { + self.request_with_body(data, &request::Add) + } + /// List available commands that the server accepts. /// #[inline] - pub fn commands(&self) -> AsyncResponse<response::CommandsResponse> { + pub fn commands(&self) -> Result<AsyncResponse<response::CommandsResponse>, Error> { self.request(&request::Commands) } /// List the contents of an Ipfs multihash. /// #[inline] - pub fn ls(&self, path: Option<&str>) -> AsyncResponse<response::LsResponse> { + pub fn ls(&self, path: Option<&str>) -> Result<AsyncResponse<response::LsResponse>, Error> { self.request(&request::LsRequest(path)) } /// Returns information about the Ipfs server version. /// #[inline] - pub fn version(&self) -> AsyncResponse<response::VersionResponse> { + pub fn version(&self) -> Result<AsyncResponse<response::VersionResponse>, Error> { self.request(&request::Version) } } diff --git a/ipfs-api/src/lib.rs b/ipfs-api/src/lib.rs index ed0303c..37f0de2 100644 --- a/ipfs-api/src/lib.rs +++ b/ipfs-api/src/lib.rs @@ -1,5 +1,5 @@ extern crate futures; -extern crate hyper; +extern crate reqwest; extern crate serde; #[macro_use] extern crate serde_derive; diff --git a/ipfs-api/src/request/add.rs b/ipfs-api/src/request/add.rs new file mode 100644 index 0000000..7237b55 --- /dev/null +++ b/ipfs-api/src/request/add.rs @@ -0,0 +1,11 @@ +use request::ApiRequest; + + +pub struct Add; + +impl ApiRequest for Add { + #[inline] + fn path() -> &'static str { + "/add" + } +} diff --git a/ipfs-api/src/request/mod.rs b/ipfs-api/src/request/mod.rs index 05a6f02..7681a78 100644 --- a/ipfs-api/src/request/mod.rs +++ b/ipfs-api/src/request/mod.rs @@ -1,8 +1,10 @@ +pub use self::add::*; pub use self::commands::*; pub use self::ls::*; pub use self::version::*; +mod add; mod commands; mod ls; mod version; diff --git a/ipfs-api/src/response/add.rs b/ipfs-api/src/response/add.rs index 388c2a6..d49d9a3 100644 --- a/ipfs-api/src/response/add.rs +++ b/ipfs-api/src/response/add.rs @@ -1,4 +1,4 @@ -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct AddResponse { pub name: String, diff --git a/ipfs-api/src/response/error.rs b/ipfs-api/src/response/error.rs index 3eb8f1b..e56c249 100644 --- a/ipfs-api/src/response/error.rs +++ b/ipfs-api/src/response/error.rs @@ -1,18 +1,25 @@ -use hyper; +use reqwest; use serde_json; use std::fmt::{self, Display, Formatter}; #[derive(Debug)] pub enum Error { - Http(hyper::error::Error), + Http(reqwest::Error), Parse(serde_json::Error), + Url(reqwest::UrlError), Api(ApiError), Uncategorized(String), } -impl From<hyper::error::Error> for Error { - fn from(error: hyper::error::Error) -> Self { +impl From<reqwest::UrlError> for Error { + fn from(error: reqwest::UrlError) -> Self { + Error::Url(error) + } +} + +impl From<reqwest::Error> for Error { + fn from(error: reqwest::Error) -> Self { Error::Http(error) } } @@ -40,6 +47,7 @@ impl ::std::error::Error for Error { match *self { Error::Http(_) => "an http error occured", Error::Parse(_) => "an error occursed while parsing the api response", + Error::Url(_) => "an error occured while parsing the request url", Error::Api(_) => "an api error occured", Error::Uncategorized(_) => "an unknown error occured", } |