1use std::error::Error;
4use std::fmt;
5
6use serde_json::Value;
7
8use client::response::{FromResponse, ParseError};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum OAuth2ErrorCode {
15 InvalidRequest,
19
20 InvalidClient,
23
24 InvalidGrant,
28
29 UnauthorizedClient,
31
32 UnsupportedGrantType,
34
35 InvalidScope,
38
39 Unrecognized(String),
41}
42
43impl<'a> From<&'a str> for OAuth2ErrorCode {
44 fn from(s: &str) -> OAuth2ErrorCode {
45 match s {
46 "invalid_request" => OAuth2ErrorCode::InvalidRequest,
47 "invalid_client" => OAuth2ErrorCode::InvalidClient,
48 "invalid_grant" => OAuth2ErrorCode::InvalidGrant,
49 "unauthorized_client" => OAuth2ErrorCode::UnauthorizedClient,
50 "unsupported_grant_type" => OAuth2ErrorCode::UnsupportedGrantType,
51 "invalid_scope" => OAuth2ErrorCode::InvalidScope,
52 s => OAuth2ErrorCode::Unrecognized(s.to_owned()),
53 }
54 }
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct OAuth2Error {
62 pub code: OAuth2ErrorCode,
64
65 pub description: Option<String>,
67
68 pub uri: Option<String>,
70}
71
72impl fmt::Display for OAuth2Error {
73 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
74 write!(f, "{:?}", self.code)?;
75 if let Some(ref description) = self.description {
76 write!(f, ": {}", description)?;
77 }
78 if let Some(ref uri) = self.uri {
79 write!(f, " ({})", uri)?;
80 }
81 Ok(())
82 }
83}
84
85impl Error for OAuth2Error {
86 fn description(&self) -> &str { "OAuth 2.0 API error" }
87}
88
89impl FromResponse for OAuth2Error {
90 fn from_response(json: &Value) -> Result<Self, ParseError> {
91 let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
92
93 let code = obj.get("error")
94 .and_then(Value::as_str)
95 .ok_or(ParseError::ExpectedFieldType("error", "string"))?;
96 let description = obj.get("error_description").and_then(Value::as_str);
97 let uri = obj.get("error_uri").and_then(Value::as_str);
98
99 Ok(OAuth2Error {
100 code: code.into(),
101 description: description.map(Into::into),
102 uri: uri.map(Into::into),
103 })
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use client::response::{FromResponse, ParseError};
110 use super::{OAuth2Error, OAuth2ErrorCode};
111
112 #[test]
113 fn from_response_empty() {
114 let json = "{}".parse().unwrap();
115 assert_eq!(
116 ParseError::ExpectedFieldType("error", "string"),
117 OAuth2Error::from_response(&json).unwrap_err()
118 );
119 }
120
121 #[test]
122 fn from_response() {
123 let json = r#"{"error":"invalid_request"}"#.parse().unwrap();
124 assert_eq!(
125 OAuth2Error {
126 code: OAuth2ErrorCode::InvalidRequest,
127 description: None,
128 uri: None,
129 },
130 OAuth2Error::from_response(&json).unwrap()
131 );
132 }
133
134 #[test]
135 fn from_response_with_description() {
136 let json = r#"{"error":"invalid_request","error_description":"foo"}"#
137 .parse()
138 .unwrap();
139 assert_eq!(
140 OAuth2Error {
141 code: OAuth2ErrorCode::InvalidRequest,
142 description: Some(String::from("foo")),
143 uri: None,
144 },
145 OAuth2Error::from_response(&json).unwrap()
146 );
147 }
148
149 #[test]
150 fn from_response_with_uri() {
151 let json = r#"{"error":"invalid_request","error_uri":"http://example.com"}"#
152 .parse()
153 .unwrap();
154 assert_eq!(
155 OAuth2Error {
156 code: OAuth2ErrorCode::InvalidRequest,
157 description: None,
158 uri: Some(String::from("http://example.com")),
159 },
160 OAuth2Error::from_response(&json).unwrap()
161 );
162 }
163}