use bcrypt::BcryptError; use diesel::result::Error as DieselErr; use serde_json::Value; use std::convert::From; use std::error::Error as StdError; use std::fmt; #[derive(Debug)] pub struct StravaApiError { status: reqwest::StatusCode, code: String, field: String, value: Value, } impl StravaApiError { pub fn new(status: reqwest::StatusCode, value: Value) -> StravaApiError { let first_error = &value["errors"][0]; let code = first_error["code"] .as_str() .unwrap_or("unknown") .to_string(); let field = first_error["field"] .as_str() .unwrap_or("unknown") .to_string(); StravaApiError { status, code, field, value, } } } impl fmt::Display for StravaApiError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "field '{}' has error '{}'", self.field, self.code) } } #[cfg(test)] mod tests { use super::*; #[test] fn strava_api_error_unknown() { let data = r#""insane input""#; let json = serde_json::from_str(data).unwrap(); let error = Error::StravaApiError(StravaApiError::new(reqwest::StatusCode::UNAUTHORIZED, json)); assert_eq!("field 'unknown' has error 'unknown'", format!("{}", error)); } #[test] fn strava_api_error_invalid() { let data = r#" { "errors":[ {"code":"invalid", "field":"access_token", "resource":"Athlete"} ], "message":"Authorization Error" }"#; let json = serde_json::from_str(data).unwrap(); let error = Error::StravaApiError(StravaApiError::new(reqwest::StatusCode::UNAUTHORIZED, json)); assert_eq!( "field 'access_token' has error 'invalid'", format!("{}", error) ); } } #[derive(Debug)] pub enum Error { DieselError(DieselErr), PasswordError(BcryptError), CommunicationError(reqwest::Error), ParseError(serde_json::error::Error), ParseTimeError(chrono::format::ParseError), TemplateError(String, Value), StravaApiError(StravaApiError), UnexpectedJson(Value), AlreadyExists, NotFound, InternalError, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Error::DieselError(ref e) => e.fmt(f), Error::PasswordError(ref e) => e.fmt(f), Error::CommunicationError(ref e) => e.fmt(f), Error::ParseError(ref e) => e.fmt(f), Error::ParseTimeError(ref e) => e.fmt(f), Error::TemplateError(ref s, _) => f.write_str(&s), Error::UnexpectedJson(_) => f.write_str("UnexpectedJson"), Error::StravaApiError(ref e) => e.fmt(f), Error::AlreadyExists => f.write_str("AlreadyExists"), Error::NotFound => f.write_str("NotFound"), Error::InternalError => f.write_str("InternalError"), } } } impl From for Error { fn from(e: chrono::format::ParseError) -> Error { Error::ParseTimeError(e) } } impl From for Error { fn from(e: StravaApiError) -> Error { Error::StravaApiError(e) } } impl From for Error { fn from(e: serde_json::error::Error) -> Error { Error::ParseError(e) } } impl From for Error { fn from(e: reqwest::Error) -> Error { Error::CommunicationError(e) } } impl From for Error { fn from(e: DieselErr) -> Error { match e { DieselErr::NotFound => Error::NotFound, e => Error::DieselError(e), } } } impl From for Error { fn from(e: BcryptError) -> Error { Error::PasswordError(e) } } impl StdError for Error {}