use std::{fmt, str};
use std::fmt::Display;
use json;
use reqwest;
error_chain! {
errors {
Api(err: ApiError) {
description("API error")
display("API error - {}", err.message)
}
Http(context: String) {
description("HTTP error")
display("HTTP error {}", context)
}
InvalidBaseUrl {
description("invalid base URL")
}
InvalidDataUri(uri: String) {
description("invalid data URI")
display("invalid data URI '{}'", uri)
}
InvalidAlgoUri(uri: String) {
description("invalid algorithm URI")
display("invalid algorithm URI: {}", &uri)
}
DecodeJson(item: &'static str) {
description("json decode error")
display("failed to decode {} json", item)
}
EncodeJson(item: &'static str) {
description("json encode error")
display("failed to encode {} as json", item)
}
DecodeBase64(item: &'static str) {
description("base64 error")
display("failed to decode {} as base64", item)
}
Io(context: String) {
description("I/O error")
display("I/O error {}", context)
}
InvalidContentType(t: String) {
description("invalid content type")
display("invalid content type: '{}'", t)
}
MismatchedContentType(expected: &'static str) {
description("mismatched content type")
display("content did not match content type: '{}'", expected)
}
UnexpectedContentType(expected: &'static str, actual: String) {
description("unexpected content type")
display("expected content type '{}', received '{}'", expected, actual)
}
NotFound(url: reqwest::Url) {
description("404 Not Found")
display("404 Not Found ({})", url)
}
MissingDataType {
description("API response missing data type")
}
InvalidDataType(t: String) {
description("invalid data type")
display("API responded with invalid data type: '{}'", t)
}
UnexpectedDataType(expected: &'static str, actual: String) {
description("unexpected data type")
display("expected API response with data type '{}', received '{}'", expected, actual)
}
UnsupportedInput {
description("unsupported input type")
}
#[doc(hidden)]
__DontMatchMe {}
}
}
#[cfg_attr(feature="with-serde", derive(Deserialize))]
#[cfg_attr(feature="with-rustc-serialize", derive(RustcDecodable))]
#[derive(Debug)]
pub struct ApiError {
pub message: String,
pub stacktrace: Option<String>,
}
impl Display for ApiError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.stacktrace {
Some(ref trace) => write!(f, "{}\n{}", self.message, trace),
None => write!(f, "{}", self.message),
}
}
}
#[cfg_attr(feature="with-serde", derive(Deserialize))]
#[cfg_attr(feature="with-rustc-serialize", derive(RustcDecodable))]
#[derive(Debug)]
#[doc(hidden)]
pub struct ApiErrorResponse {
pub error: ApiError,
}
#[doc(hidden)]
pub fn decode(json_str: &str) -> Error {
let decoded_error = json::decode_str::<ApiErrorResponse>(json_str);
match decoded_error.chain_err(|| ErrorKind::DecodeJson("api error response")) {
Ok(err_res) => ErrorKind::Api(err_res.error).into(),
Err(err) => err,
}
}