Skip to content

Commit

Permalink
feat: add initial functionality
Browse files Browse the repository at this point in the history
Add initial functionality.
Add optional `hyper` dependency for the `hyper::Client` implementation of
`WolframAlphaRequestSender`.
  • Loading branch information
indiv0 committed Jun 22, 2016
1 parent fd8217c commit 6eefd92
Show file tree
Hide file tree
Showing 5 changed files with 1,315 additions and 0 deletions.
31 changes: 31 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "wolfram_alpha"
version = "0.1.0"
authors = ["Nikita Pekin <contact@nikitapek.in>"]

[dependencies]
lazy_static = "0.2.1"
log = "0.3.6"
serde = "0.7.4"
serde_macros = "0.7.0"
serde_xml = "0.7.0"

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

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

[dependencies.url]
features = ["serde"]
version = "1.1.1"

[features]
default = ["hyper"]
nightly = []
nightly-testing = [
"clippy",
"nightly",
]
132 changes: 132 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright (c) 2016 Nikita Pekin and the wolfram_alpha_rs contributors
// See the README.md file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::result::Result as StdResult;

use serde_xml;

/// A convenient alias type for results for `wolfram_alpha`.
pub type Result<T> = StdResult<T, Error>;

/// Represents errors which occur while using the Wolfram|Alpha API.
#[derive(Debug)]
pub enum Error {
/// Error sending a HTTP request to Wolfram|Alpha.
HttpRequest(HttpRequestError),
/// An IO error was encountered.
Io(io::Error),
/// Error while serializing or deserializing XML.
Xml(serde_xml::Error),
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::HttpRequest(ref e) => e.fmt(f),
Error::Io(ref e) => e.fmt(f),
Error::Xml(ref e) => e.fmt(f),
}
}
}

impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::HttpRequest(ref e) => e.description(),
Error::Io(ref e) => e.description(),
Error::Xml(ref e) => e.description(),
}
}

fn cause(&self) -> Option<&StdError> {
match *self {
Error::HttpRequest(ref e) => e.cause(),
Error::Io(ref e) => e.cause(),
Error::Xml(ref e) => e.cause(),
}
}
}

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

impl From<io::Error> for Error {
fn from(error: io::Error) -> Error {
Error::Io(error)
}
}

impl From<serde_xml::Error> for Error {
fn from(error: serde_xml::Error) -> Error {
Error::Xml(error)
}
}

// Implement `PartialEq` manually, since `std::io::Error` does not implement it.
impl PartialEq<Error> for Error {
fn eq(&self, other: &Error) -> bool {
use self::Error::*;

match (self, other) {
(&HttpRequest(_), &HttpRequest(_)) |
(&Io(_), &Io(_)) |
(&Xml(_), &Xml(_)) => true,
_ => false,
}
}
}

/// 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 {
/// An error occuring during network IO operations.
Io(io::Error),
/// Any other error occuring during an HTTP request.
Other(Box<StdError>),
}

impl fmt::Display for HttpRequestError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
HttpRequestError::Io(ref e) => e.fmt(f),
HttpRequestError::Other(ref e) => e.fmt(f),
}
}
}

impl StdError for HttpRequestError {
fn description(&self) -> &str {
match *self {
HttpRequestError::Io(ref e) => e.description(),
HttpRequestError::Other(ref e) => e.description(),
}
}

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

impl From<io::Error> for HttpRequestError {
fn from(error: io::Error) -> HttpRequestError {
HttpRequestError::Io(error)
}
}
132 changes: 132 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright (c) 2016 Nikita Pekin and the wolfram_alpha_rs contributors
// See the README.md file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(missing_docs)]
#![deny(non_camel_case_types)]
#![deny(warnings)]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "nightly", feature(plugin))]
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

//! A library providing Rust bindings for the Wolfram|Alpha web API.
//!
//! The library provides a `WolframAlphaRequestSender` trait which can be
//! 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 hyper;

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate serde;
extern crate serde_xml;
extern crate url;

mod error;

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

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

use std::collections::HashMap;
use std::fmt::Debug;

use serde::Deserialize;

fn parse_wolfram_alpha_response<T>(response: &str) -> Result<T>
where T: Debug + Deserialize,
{
let parsed_response = try!(serde_xml::from_str(response));
trace!("Parsed response: {:?}", parsed_response);
Ok(parsed_response)
}

/// Functionality for sending requests to Wolfram|Alpha via HTTP.
///
/// Should be implemented for clients to send requests to Wolfram|Alpha.
pub trait WolframAlphaRequestSender {
/// Performs an API call to Wolfram|Alpha.
///
/// 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>;

/// 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> {
params.insert("appid", app_id);
self.send(method, params)
}
}

#[cfg(feature = "hyper")]
mod hyper_support {
use std::collections::HashMap;
use std::io::Read;

use hyper;
use url::Url;

use error::{HttpRequestError, HttpRequestResult};

use super::WolframAlphaRequestSender;

impl WolframAlphaRequestSender for hyper::Client {
fn send<'a>(&self, method: &str, params: &mut HashMap<&str, &'a str>)
-> HttpRequestResult<String>
{
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 = try!(self.get(url).send());
let mut result = String::new();
try!(response.read_to_string(&mut result));
trace!("Query result: {}", result);

Ok(result)
}
}

impl From<hyper::Error> for HttpRequestError {
fn from(error: hyper::Error) -> HttpRequestError {
match error {
hyper::Error::Io(e) => HttpRequestError::Io(e),
e => HttpRequestError::Other(Box::new(e)),
}
}
}
}

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

0 comments on commit 6eefd92

Please sign in to comment.