Skip to content

Commit eafd37a

Browse files
authored
Merge pull request #23 from indiv0/dev
Implement Async API
2 parents bfaa16b + 0476562 commit eafd37a

File tree

7 files changed

+131
-49
lines changed

7 files changed

+131
-49
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ env:
5454
global:
5555
- RUST_BACKTRACE=1
5656
# override the default '--features unstable' used for the nightly branch
57-
- TRAVIS_CARGO_NIGHTLY_FEATURE="nightly-testing"
57+
- TRAVIS_CARGO_NIGHTLY_FEATURE="nightly"
5858
# encrypted Github token for doc upload
5959
- secure: DHSbYBAuYcv4bZUF5Rnf85dvHQ1tlpphj/TBfwr4EL/FcZufjllo2G8MwBnofeh1aB5rH2ZtLLSJx/2MPh7ShKM36NhtYZvbpjWyuocazce5VyzdjaZtXQ25F8CNUzG48iItFHLgLWCGff047SJwplVIXytkG8bDwN6jp3BHWTmkXtvCJH3WS34ZuVZovagwuv9iEBr+QOOxyX5qGzWIhtjTVSd4aA0g04gfdSqbCYbhSWF12LiFEP/dNYEmVm7wIMm59kfrh1hGUPJ27tiIwXww3lmLXrY3y4vqEKYE+Zmu+VVAb0gDDnZ5REBHlAnPk1R8gQXgTULkkKNAm45q2aKU2/glRl3dKq1yG1XB3/OdbwlJXX5wKOS58/kqsSaO4Jw5/8dryvlM5pympqRpAlvtTEno+c3d25Km1GxozxJ4C2xNZniduccaO3BTLnIZPs9fTKlZb3Q1+cr+vfOpyf2Gvhvbc9q0k4l0uYfsET/o/tD8yAScymAAm49u4WDQjqldUAUk1dtPC+FssN3gwURSibWP8K5LAkCjBc1xlxdRXz18h8Qg8bgANm/yifCzPm8YGeDBZLD8He/36l4T+xdua/zIWSRlE8W2nhuaT5u+uuLCZj5eygmggGbnCwTXCeGPjZVcQ4C8RfDIKJ/oltIs9lzX1XUqcpN4IBbhg+c=
6060
branches:

Cargo.toml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,28 +30,30 @@ optional = true
3030
version = "0.8.23"
3131

3232
[dependencies]
33+
futures = "0.1.17"
34+
hyper = "0.11.7"
3335
log = "0.3.6"
3436
serde = "0.8.23"
3537
serde_xml = "0.8.1"
38+
tokio-core = "0.1.10"
3639

3740
[dependencies.clippy]
3841
optional = true
3942
version = "0.0"
4043

41-
[dependencies.hyper]
42-
optional = true
43-
version = "0.10.4"
44-
4544
[dependencies.serde_derive]
4645
optional = true
4746
version = "0.8.23"
4847

4948
[dependencies.url]
5049
features = ["serde"]
51-
version = "1.4.0"
50+
version = "1.6.0"
51+
52+
[dev-dependencies]
53+
hyper-tls = "0.1.2"
5254

5355
[features]
54-
default = ["hyper", "with-syntex"]
56+
default = ["with-syntex"]
5557
nightly = ["serde_derive"]
5658
nightly-testing = ["clippy", "nightly"]
5759
with-syntex = ["serde_codegen"]

examples/simple.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) 2017 Nikita Pekin and the wolfram_alpha_rs contributors
2+
// See the README.md file at the top-level directory of this distribution.
3+
//
4+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7+
// option. This file may not be copied, modified, or distributed
8+
// except according to those terms.
9+
10+
extern crate futures;
11+
extern crate hyper;
12+
extern crate hyper_tls;
13+
extern crate tokio_core;
14+
extern crate wolfram_alpha;
15+
16+
use futures::future::Future;
17+
use hyper::Client;
18+
use hyper_tls::HttpsConnector;
19+
use tokio_core::reactor::Core;
20+
use wolfram_alpha::query::query;
21+
22+
const APP_ID: &str = "YOUR_APP_ID";
23+
24+
fn main() {
25+
let mut core = Core::new().unwrap();
26+
let client = Client::configure()
27+
.connector(HttpsConnector::new(4, &core.handle()).unwrap())
28+
.build(&core.handle());
29+
30+
let input = "distance from the Sun to the Earth";
31+
let work = query(&client, APP_ID, input, None)
32+
.and_then(|res| {
33+
let subpod = &res.pod.unwrap()[1].subpod[0];
34+
let plaintext = subpod.plaintext.as_ref().unwrap();
35+
println!("Distance from the Sun to the Earth: {:?}", plaintext);
36+
Ok(())
37+
});
38+
39+
core.run(work).unwrap();
40+
}

src/error.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
// option. This file may not be copied, modified, or distributed
88
// except according to those terms.
99

10+
use hyper::error::UriError;
1011
use serde_xml;
1112
use std::error::Error as StdError;
1213
use std::fmt;
1314
use std::io;
1415
use std::result::Result as StdResult;
16+
use std::str::Utf8Error;
1517

1618
/// A convenient alias type for results for `wolfram_alpha`.
1719
pub type Result<T> = StdResult<T, Error>;
@@ -87,12 +89,13 @@ impl PartialEq<Error> for Error {
8789
}
8890
}
8991

90-
/// A convenient alias type for results of HTTP requests.
91-
pub type HttpRequestResult<T> = StdResult<T, HttpRequestError>;
92-
9392
/// Represents errors which occur when sending an HTTP request to Wolfram|Alpha.
9493
#[derive(Debug)]
9594
pub enum HttpRequestError {
95+
/// Error parsing a URL string into a `hyper::Uri`.
96+
UriError(UriError),
97+
/// Error parsing a bytes into a UTF-8 string.
98+
Utf8Error(Utf8Error),
9699
/// An error occuring during network IO operations.
97100
Io(io::Error),
98101
/// Any other error occuring during an HTTP request.
@@ -103,6 +106,8 @@ impl fmt::Display for HttpRequestError {
103106
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104107
match *self {
105108
HttpRequestError::Io(ref e) => e.fmt(f),
109+
HttpRequestError::UriError(ref e) => e.fmt(f),
110+
HttpRequestError::Utf8Error(ref e) => e.fmt(f),
106111
HttpRequestError::Other(ref e) => e.fmt(f),
107112
}
108113
}
@@ -112,18 +117,34 @@ impl StdError for HttpRequestError {
112117
fn description(&self) -> &str {
113118
match *self {
114119
HttpRequestError::Io(ref e) => e.description(),
120+
HttpRequestError::UriError(ref e) => e.description(),
121+
HttpRequestError::Utf8Error(ref e) => e.description(),
115122
HttpRequestError::Other(ref e) => e.description(),
116123
}
117124
}
118125

119126
fn cause(&self) -> Option<&StdError> {
120127
match *self {
121128
HttpRequestError::Io(ref e) => e.cause(),
129+
HttpRequestError::UriError(ref e) => e.cause(),
130+
HttpRequestError::Utf8Error(ref e) => e.cause(),
122131
HttpRequestError::Other(ref e) => e.cause(),
123132
}
124133
}
125134
}
126135

136+
impl From<UriError> for HttpRequestError {
137+
fn from(error: UriError) -> HttpRequestError {
138+
HttpRequestError::UriError(error)
139+
}
140+
}
141+
142+
impl From<Utf8Error> for HttpRequestError {
143+
fn from(error: Utf8Error) -> HttpRequestError {
144+
HttpRequestError::Utf8Error(error)
145+
}
146+
}
147+
127148
impl From<io::Error> for HttpRequestError {
128149
fn from(error: io::Error) -> HttpRequestError {
129150
HttpRequestError::Io(error)

src/lib.rs

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// option. This file may not be copied, modified, or distributed
88
// except according to those terms.
99

10-
#![deny(missing_docs, non_camel_case_types, warnings)]
10+
#![deny(missing_docs, non_camel_case_types)]
1111
#![cfg_attr(feature = "clippy", feature(plugin))]
1212
#![cfg_attr(feature = "clippy", plugin(clippy))]
1313
#![cfg_attr(feature = "nightly", feature(custom_derive, proc_macro))]
@@ -18,34 +18,30 @@
1818
//! implemented by various request senders. These implementations may then be
1919
//! used to execute requests to the API.
2020
//!
21-
//! If the `hyper` feature is enabled during compilation, then this library
22-
//! provides an implementation of the `WolframAlphaRequestSender` trait for
23-
//! the `hyper::Client` of the [`hyper`](https://github.com/hyperium/hyper)
24-
//! library.
25-
//!
2621
//! Response bodies are deserialized from XML into structs via the
2722
//! [`serde_xml`](https://github.com/serde-rs/xml) library.
2823
29-
#[cfg(feature = "hyper")]
24+
extern crate futures;
3025
extern crate hyper;
31-
3226
#[macro_use]
3327
extern crate log;
3428
extern crate serde;
3529
#[cfg(feature = "nightly")]
3630
#[macro_use]
3731
extern crate serde_derive;
3832
extern crate serde_xml;
33+
extern crate tokio_core;
3934
extern crate url;
4035

4136
mod error;
4237

43-
pub use self::error::{Error, HttpRequestError, HttpRequestResult, Result};
38+
pub use self::error::{Error, HttpRequestError, Result};
4439

4540
pub mod model;
4641
pub mod query;
4742
// TODO: implement the `validate_query` function.
4843

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

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

83-
#[cfg(feature = "hyper")]
8480
mod hyper_support {
85-
use error::{HttpRequestError, HttpRequestResult};
86-
use hyper;
81+
use error::HttpRequestError;
82+
use futures::{self, Future, Stream};
83+
use hyper::{self, Uri};
84+
use hyper::client::Connect;
8785
use std::collections::HashMap;
88-
use std::io::Read;
86+
use std::str::{self, FromStr};
8987
use super::WolframAlphaRequestSender;
9088
use url::Url;
9189

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

98-
url.query_pairs_mut().extend_pairs(params.into_iter());
99-
100-
trace!("Sending query \"{:?}\" to url: {}", params, url);
101-
let mut response = self.get(url).send()?;
102-
let mut result = String::new();
103-
response.read_to_string(&mut result)?;
104-
trace!("Query result: {}", result);
105-
106-
Ok(result)
99+
url.query_pairs_mut().extend_pairs((&mut params).into_iter());
100+
101+
let uri = Uri::from_str(url.as_ref()).map_err(From::from);
102+
let res = futures::done(uri)
103+
.and_then(move |uri| {
104+
trace!("Sending query \"{:?}\" to URL: {}", params, url.as_ref());
105+
self.get(uri)
106+
})
107+
.and_then(|res| {
108+
trace!("Response: {}", res.status());
109+
110+
res.body().concat2()
111+
})
112+
.map_err(From::from)
113+
.map(|body| {
114+
str::from_utf8(&body)
115+
.map_err(From::from)
116+
.map(|string| string.to_string())
117+
})
118+
.and_then(|string| string);
119+
120+
Box::new(res)
107121
}
108122
}
109123

@@ -117,5 +131,4 @@ mod hyper_support {
117131
}
118132
}
119133

120-
#[cfg(feature = "hyper")]
121134
pub use hyper_support::*;

src/model.in.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ impl Deserialize for States {
578578
if state.is_none() {
579579
state = Some(more_state);
580580
} else {
581-
if let Some(mut state) = state.as_mut() {
581+
if let Some(state) = state.as_mut() {
582582
state.append(&mut more_state);
583583
}
584584
}
@@ -595,7 +595,7 @@ impl Deserialize for States {
595595
if statelist.is_none() {
596596
statelist = Some(Some(more_statelist));
597597
} else {
598-
if let Some(mut statelist) = statelist.as_mut() {
598+
if let Some(statelist) = statelist.as_mut() {
599599
if let Some(statelist) = statelist.as_mut() {
600600
statelist.append(&mut more_statelist);
601601
}

src/query.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
//! For more information, see [Wolfram|Alpha's API
1313
//! documentation](http://products.wolframalpha.com/api/documentation.html).
1414
15-
use error::Result;
15+
use error::Error;
16+
use futures::Future;
1617
use model::QueryResult;
1718
use std::collections::HashMap;
1819
use super::{WolframAlphaRequestSender, parse_wolfram_alpha_response};
@@ -49,10 +50,10 @@ pub struct OptionalQueryParameters<'a> {
4950
}
5051

5152
/// Performs a query to the Wolfram|Alpha API.
52-
pub fn query<R>(
53-
client: &R, appid: &str, input: &str,
54-
optional_query_parameters: Option<OptionalQueryParameters>
55-
) -> Result<QueryResult>
53+
pub fn query<'a, R>(
54+
client: &'a R, appid: &'a str, input: &'a str,
55+
optional_query_parameters: Option<OptionalQueryParameters<'a>>
56+
) -> Box<'a + Future<Item = QueryResult, Error = Error>>
5657
where R: WolframAlphaRequestSender,
5758
{
5859
let mut params = HashMap::new();
@@ -90,6 +91,11 @@ pub fn query<R>(
9091
}
9192
}
9293

93-
let response = client.send_authed("query", appid, &mut params)?;
94-
parse_wolfram_alpha_response(&response)
94+
let res = client.send_authed("query", appid, params)
95+
.map_err(From::from)
96+
.and_then(|res| {
97+
parse_wolfram_alpha_response(&res)
98+
});
99+
100+
Box::new(res)
95101
}

0 commit comments

Comments
 (0)