Skip to content

Commit

Permalink
impr: switch to async API
Browse files Browse the repository at this point in the history
Update the lib to use the async hyper library.
Replace the sync API with an async API for use with futures.
  • Loading branch information
indiv0 committed Nov 21, 2017
1 parent d9c994a commit b6d2e9a
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 45 deletions.
14 changes: 8 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,30 @@ optional = true
version = "0.8.23"

[dependencies]
futures = "0.1.17"
hyper = "0.11.7"
log = "0.3.6"
serde = "0.8.23"
serde_xml = "0.8.1"
tokio-core = "0.1.10"

[dependencies.clippy]
optional = true
version = "0.0"

[dependencies.hyper]
optional = true
version = "0.10.4"

[dependencies.serde_derive]
optional = true
version = "0.8.23"

[dependencies.url]
features = ["serde"]
version = "1.4.0"
version = "1.6.0"

[dev-dependencies]
hyper-tls = "0.1.2"

[features]
default = ["hyper", "with-syntex"]
default = ["with-syntex"]
nightly = ["serde_derive"]
nightly-testing = ["clippy", "nightly"]
with-syntex = ["serde_codegen"]
27 changes: 24 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use hyper::error::UriError;
use serde_xml;
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::result::Result as StdResult;
use std::str::Utf8Error;

/// A convenient alias type for results for `wolfram_alpha`.
pub type Result<T> = StdResult<T, Error>;
Expand Down Expand Up @@ -87,12 +89,13 @@ impl PartialEq<Error> for Error {
}
}

/// A convenient alias type for results of HTTP requests.
pub type HttpRequestResult<T> = StdResult<T, HttpRequestError>;

/// Represents errors which occur when sending an HTTP request to Wolfram|Alpha.
#[derive(Debug)]
pub enum HttpRequestError {
/// Error parsing a URL string into a `hyper::Uri`.
UriError(UriError),
/// Error parsing a bytes into a UTF-8 string.
Utf8Error(Utf8Error),
/// An error occuring during network IO operations.
Io(io::Error),
/// Any other error occuring during an HTTP request.
Expand All @@ -103,6 +106,8 @@ impl fmt::Display for HttpRequestError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
HttpRequestError::Io(ref e) => e.fmt(f),
HttpRequestError::UriError(ref e) => e.fmt(f),
HttpRequestError::Utf8Error(ref e) => e.fmt(f),
HttpRequestError::Other(ref e) => e.fmt(f),
}
}
Expand All @@ -112,18 +117,34 @@ impl StdError for HttpRequestError {
fn description(&self) -> &str {
match *self {
HttpRequestError::Io(ref e) => e.description(),
HttpRequestError::UriError(ref e) => e.description(),
HttpRequestError::Utf8Error(ref e) => e.description(),
HttpRequestError::Other(ref e) => e.description(),
}
}

fn cause(&self) -> Option<&StdError> {
match *self {
HttpRequestError::Io(ref e) => e.cause(),
HttpRequestError::UriError(ref e) => e.cause(),
HttpRequestError::Utf8Error(ref e) => e.cause(),
HttpRequestError::Other(ref e) => e.cause(),
}
}
}

impl From<UriError> for HttpRequestError {
fn from(error: UriError) -> HttpRequestError {
HttpRequestError::UriError(error)
}
}

impl From<Utf8Error> for HttpRequestError {
fn from(error: Utf8Error) -> HttpRequestError {
HttpRequestError::Utf8Error(error)
}
}

impl From<io::Error> for HttpRequestError {
fn from(error: io::Error) -> HttpRequestError {
HttpRequestError::Io(error)
Expand Down
71 changes: 42 additions & 29 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,30 @@
//! implemented by various request senders. These implementations may then be
//! used to execute requests to the API.
//!
//! If the `hyper` feature is enabled during compilation, then this library
//! provides an implementation of the `WolframAlphaRequestSender` trait for
//! the `hyper::Client` of the [`hyper`](https://github.com/hyperium/hyper)
//! library.
//!
//! Response bodies are deserialized from XML into structs via the
//! [`serde_xml`](https://github.com/serde-rs/xml) library.

#[cfg(feature = "hyper")]
extern crate futures;
extern crate hyper;

#[macro_use]
extern crate log;
extern crate serde;
#[cfg(feature = "nightly")]
#[macro_use]
extern crate serde_derive;
extern crate serde_xml;
extern crate tokio_core;
extern crate url;

mod error;

pub use self::error::{Error, HttpRequestError, HttpRequestResult, Result};
pub use self::error::{Error, HttpRequestError, Result};

pub mod model;
pub mod query;
// TODO: implement the `validate_query` function.

use futures::Future;
use serde::Deserialize;
use std::collections::HashMap;
use std::fmt::Debug;
Expand All @@ -66,44 +62,62 @@ pub trait WolframAlphaRequestSender {
///
/// Takes a map of parameters which get appended to the request as query
/// parameters. Returns the response body string.
fn send<'a>(&self, method: &str, params: &mut HashMap<&str, &'a str>)
-> HttpRequestResult<String>;
fn send<'a>(&'a self, method: &str, params: HashMap<&'a str, &'a str>)
-> Box<'a + Future<Item = String, Error = HttpRequestError>>;

/// Make an API call to Wolfram|Alpha that contains the configured App ID.
///
/// Takes a map of parameters which get appended to the request as query
/// parameters. Returns the response body string.
fn send_authed<'a>(&self, method: &str, app_id: &'a str, params: &mut HashMap<&str, &'a str>)
-> HttpRequestResult<String> {
fn send_authed<'a>(&'a self, method: &str, app_id: &'a str, mut params: HashMap<&'a str, &'a str>)
-> Box<'a + Future<Item = String, Error = HttpRequestError>>
{
params.insert("appid", app_id);
self.send(method, params)
}
}

#[cfg(feature = "hyper")]
mod hyper_support {
use error::{HttpRequestError, HttpRequestResult};
use hyper;
use error::HttpRequestError;
use futures::{self, Future, Stream};
use hyper::{self, Uri};
use hyper::client::Connect;
use std::collections::HashMap;
use std::io::Read;
use std::str::{self, FromStr};
use super::WolframAlphaRequestSender;
use url::Url;

impl WolframAlphaRequestSender for hyper::Client {
fn send<'a>(&self, method: &str, params: &mut HashMap<&str, &'a str>)
-> HttpRequestResult<String> {
impl<C> WolframAlphaRequestSender for hyper::Client<C>
where C: Connect,
{
fn send<'a>(&'a self, method: &str, mut params: HashMap<&'a str, &'a str>)
-> Box<'a + Future<Item = String, Error = HttpRequestError>>
{
let url_string = format!("https://api.wolframalpha.com/v2/{}", method);
let mut url = url_string.parse::<Url>().expect("Unable to parse URL");

url.query_pairs_mut().extend_pairs(params.into_iter());

trace!("Sending query \"{:?}\" to url: {}", params, url);
let mut response = self.get(url).send()?;
let mut result = String::new();
response.read_to_string(&mut result)?;
trace!("Query result: {}", result);

Ok(result)
url.query_pairs_mut().extend_pairs((&mut params).into_iter());

let uri = Uri::from_str(url.as_ref()).map_err(From::from);
let res = futures::done(uri)
.and_then(move |uri| {
trace!("Sending query \"{:?}\" to URL: {}", params, url.as_ref());
self.get(uri)
})
.and_then(|res| {
trace!("Response: {}", res.status());

res.body().concat2()
})
.map_err(From::from)
.map(|body| {
str::from_utf8(&body)
.map_err(From::from)
.map(|string| string.to_string())
})
.and_then(|string| string);

Box::new(res)
}
}

Expand All @@ -117,5 +131,4 @@ mod hyper_support {
}
}

#[cfg(feature = "hyper")]
pub use hyper_support::*;
20 changes: 13 additions & 7 deletions src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
//! For more information, see [Wolfram|Alpha's API
//! documentation](http://products.wolframalpha.com/api/documentation.html).

use error::Result;
use error::Error;
use futures::Future;
use model::QueryResult;
use std::collections::HashMap;
use super::{WolframAlphaRequestSender, parse_wolfram_alpha_response};
Expand Down Expand Up @@ -49,10 +50,10 @@ pub struct OptionalQueryParameters<'a> {
}

/// Performs a query to the Wolfram|Alpha API.
pub fn query<R>(
client: &R, appid: &str, input: &str,
optional_query_parameters: Option<OptionalQueryParameters>
) -> Result<QueryResult>
pub fn query<'a, R>(
client: &'a R, appid: &'a str, input: &'a str,
optional_query_parameters: Option<OptionalQueryParameters<'a>>
) -> Box<'a + Future<Item = QueryResult, Error = Error>>
where R: WolframAlphaRequestSender,
{
let mut params = HashMap::new();
Expand Down Expand Up @@ -90,6 +91,11 @@ pub fn query<R>(
}
}

let response = client.send_authed("query", appid, &mut params)?;
parse_wolfram_alpha_response(&response)
let res = client.send_authed("query", appid, params)
.map_err(From::from)
.and_then(|res| {
parse_wolfram_alpha_response(&res)
});

Box::new(res)
}

0 comments on commit b6d2e9a

Please sign in to comment.