quandl_v3/
lib.rs

1//! ## Rust bindings for Quandl v3 API.
2//!
3//! The goal of this crate is to offer a well documented, complete and easy to use interface to
4//! Quandl's RESTful API.
5//!
6//! This crate uses the `rustc_serialize` crate extensively and thus suffers from some of its
7//! limitation. Namely,
8//!
9//! * When querying for the metadata of a dataset, the field `type` will be missing. This is due to
10//!   `type` being a keyword in Rust. Use of this crate assumes knowledge of the layout of the
11//!   queried data, so that field was not very important fortunately.
12//!
13//! * Most public enum's variants have non camel case names to match the naming convention of the
14//!   API. The deserializer need the names to match to work properly, thus you will see
15//!   `Order::asc` instead of the more readable `Order::Ascending`.
16//!
17//! Some other design choices of this crate includes
18//!
19//! * No runtime checking of the query created. This crate makes it as hard as statically possible
20//!   to create an invalid query. However, the query will be checked by the Quandl API directly. On
21//!   the bright side, we forward Quandl's error messages/codes without pruning any information;
22//!   and their error-reporting is very good.
23//!
24//! * The inclusion of a `batch_query` function that allows users to submit a bunch of query at the
25//!   same time. The function returns an iterator which gives the benefit of multithreading
26//!   downloads and asynchronicity which are indispensable when doing data mining.
27//!
28//! * We use the JSON Quandl API for everything but data queries as it often returns more
29//!   information. When it comes to the data queries we use the CSV subset of the API as it is
30//!   faster and allows to use the `rust-csv` crates which allow you to define your own structs to
31//!   receive the data.
32//!
33//! ### Simple example
34//!
35//! ```rust
36//! extern crate quandl_v3;
37//!
38//! use quandl_v3::Result;
39//! use quandl_v3::prelude::*;
40//!
41//! fn main() {
42//!     let query = {
43//!         let mut query = DataQuery::new("WIKI", "AAPL");
44//!
45//!          query.order(Order::asc)
46//!               .end_date(2016, 2, 29)
47//!               .start_date(2016, 2, 1)
48//!               .column_index(4);
49//!
50//!          query
51//!     };
52//!
53//!     let response: Vec<(String, f64)> = query.send().unwrap();
54//!
55//!     // Print the date and closing price for Apple's stock for the month of February 2016.
56//!     for data in &response {
57//!         println!("{} - {}", data.0, data.1);
58//!     }
59//! }
60//! ```
61//!
62//! This crate is written in the hope it will be useful. I am in no way affiliated to Quandl and
63//! Quandl is not endorsing this crate in any way.
64//!
65//! Some of the documentation in this crate has been directly copied from Quandl's API
66//! Documentation (which is made evident from the links to that documentation directly in this
67//! crate's documentation). Those obiously remains the intellectual property of Quandl and were
68//! paraphrased to make the use of this crate simpler.
69//!
70//! [Quandl's Terms of Use](https://www.quandl.com/about/terms)
71//!
72
73extern crate zip;
74extern crate csv;
75extern crate serde;
76extern crate reqwest;
77extern crate num_cpus;
78extern crate serde_json;
79#[macro_use] extern crate serde_derive;
80#[macro_use] extern crate has;
81
82mod types;
83mod query;
84mod api_call;
85mod download;
86mod parameters;
87mod batch_query;
88
89/// This crate's public interface.
90///
91/// This exclude error handling names to avoid needless conflicts with other crates (e.g.
92/// `std::result::Result`).
93///
94pub mod prelude;
95
96use std::collections::BTreeMap;
97
98/// Crate-wide return type for functions which may fail.
99///
100pub type Result<T> = ::std::result::Result<T, Error>;
101
102/// Struct for storing a Quandl API error response as-is.
103///
104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105pub struct ApiErrorResponse {
106    /// This field contains more specific information about what went wrong. For example, it could
107    /// inform you that a `start_date` is outside a valid range.
108    ///
109    pub errors: Option<BTreeMap<String, Vec<String>>>,
110
111    /// Hold more generic failure information.
112    ///
113    pub quandl_error: QuandlError,
114}
115
116/// Struct holding Quandl's error code and corresponding message.
117///
118#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
119pub struct QuandlError {
120    /// Quandl-specific error code.
121    ///
122    pub code: String,
123
124    /// Quandl's generic error message matching the above error code.
125    ///
126    pub message: String,
127}
128
129/// Crate-wide error value. This enumerate the only four possible source of failures in this crate.
130///
131#[derive(Debug, Clone, PartialEq)]
132pub enum Error {
133    /// Is returned when Quandl's reply to a query with an error. The contained `ApiErrorResponse`
134    /// contains very verbose information about what went wrong with any specific query.
135    ///
136    ApiCallFailed(ApiErrorResponse),
137
138    /// Is returned when a problem occurs while exchanging informaiton with the Quandl's servers.
139    /// It could mean the Internet connection was lost, that the remote server closed the
140    /// connection unexpectedly, etc.
141    ///
142    /// Unfortunately, the current implementation for network connection (hyper) has very weak
143    /// error reporting and thus might leave the user confused as to why such an error is returned.
144    ///
145    DownloadFailed(String),
146
147    /// Is returned when the received value, assuming Quandl didn't respond with an error and that
148    /// there was no download error, breaks one of the parsers' assumption. Most of the time it
149    /// would be an error from `rustc_serialize` (which also does not report very meaningful errors
150    /// unfortunately) or it could also be a custom message from this library for data which didn't
151    /// met the format deserializable by the `rustc_serialize` crate.
152    ///
153    ParsingFailed(String),
154
155    /// Is returned when an I/O operation fails. This last error is highly system-dependant and
156    /// again, the error message string returned are not always very verbose.
157    ///
158    IoError(String),
159}
160
161impl ::std::error::Error for Error {
162    fn description(&self) -> &str {
163        match self {
164            &Error::ApiCallFailed(_)  => "Quandl's server responded with an error.",
165            &Error::DownloadFailed(_) => "Download failed.",
166            &Error::ParsingFailed(_)  => "Parsing data failed.",
167            &Error::IoError(_)        => "Underlying system I/O error.",
168        }
169    }
170}
171
172impl ::std::fmt::Display for Error {
173    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
174        match self {
175            &Error::ApiCallFailed(ref e) => {
176                if e.errors.is_some() && !e.errors.as_ref().unwrap().is_empty() {
177                    let (object, what) = e.errors.as_ref().unwrap().iter().next().unwrap();
178
179                    write!(f, "{}", {
180                        what.iter().fold(format!("{} - ", object), |xs, x| format!("{} {}", xs, x))
181                    })
182                } else {
183                    write!(f, "{}", e.quandl_error.message)
184                }
185            },
186
187            &Error::DownloadFailed(ref s) => {
188                write!(f, "download failed with error '{}'.", s)
189            },
190
191            &Error::ParsingFailed(ref s) => {
192                write!(f, "parsing encoded data failed with error '{}'.", s)
193            },
194
195            &Error::IoError(ref s) => {
196                write!(f, "I/O operation failed with error '{}'.", s)
197            },
198        }
199    }
200}