fix(errors): Directly pass up Serde errors for easier debugging (#16)

* fix(errors): Directly pass up Serde errors for easier debugging

* refactor(errors): Also check for API errors

* tests(errors): Add test case

* tests(upload): Ignore integration test with real Mastodon connection per default

* fix(tests): Fix error assertion after change of field ordering

* refactor(json): Create a shared function for response JSON converting
master
Klaus Purer 6 years ago committed by Aaron Power
parent 3761118f10
commit 0ee5806534
  1. 73
      src/lib.rs

@ -58,7 +58,7 @@ use std::ops;
use json::Error as SerdeError; use json::Error as SerdeError;
use reqwest::Error as HttpError; use reqwest::Error as HttpError;
use reqwest::{Client, StatusCode}; use reqwest::{Client, Response, StatusCode};
use reqwest::header::{Authorization, Bearer, Headers}; use reqwest::header::{Authorization, Bearer, Headers};
use entities::prelude::*; use entities::prelude::*;
@ -74,20 +74,11 @@ macro_rules! methods {
fn $method<T: for<'de> serde::Deserialize<'de>>(&self, url: String) fn $method<T: for<'de> serde::Deserialize<'de>>(&self, url: String)
-> Result<T> -> Result<T>
{ {
use std::io::Read; let response = self.client.$method(&url)
let mut response = self.client.$method(&url)
.headers(self.headers.clone()) .headers(self.headers.clone())
.send()?; .send()?;
let mut vec = Vec::new(); json_convert_response(response)
response.read_to_end(&mut vec)?;
if let Ok(t) = json::from_slice(&vec) {
Ok(t)
} else {
Err(Error::Api(json::from_slice(&vec)?))
}
} }
)+ )+
}; };
@ -103,7 +94,6 @@ macro_rules! route {
#[doc = "# Errors"] #[doc = "# Errors"]
/// If `access_token` is not set. /// If `access_token` is not set.
pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> { pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
use std::io::Read;
use reqwest::multipart::Form; use reqwest::multipart::Form;
let form_data = Form::new() let form_data = Form::new()
@ -111,7 +101,7 @@ macro_rules! route {
.file(stringify!($param), $param.as_ref())? .file(stringify!($param), $param.as_ref())?
)*; )*;
let mut response = self.client.post(&self.route(concat!("/api/v1/", $url))) let response = self.client.post(&self.route(concat!("/api/v1/", $url)))
.headers(self.headers.clone()) .headers(self.headers.clone())
.multipart(form_data) .multipart(form_data)
.send()?; .send()?;
@ -124,15 +114,7 @@ macro_rules! route {
return Err(Error::Server(status)); return Err(Error::Server(status));
} }
let mut vec = Vec::new(); json_convert_response(response)
response.read_to_end(&mut vec)?;
match json::from_slice::<$ret>(&vec) {
Ok(res) => Ok(res),
Err(_) => Err(Error::Api(json::from_slice(&vec)?)),
}
} }
route!{$($rest)*} route!{$($rest)*}
@ -146,7 +128,6 @@ macro_rules! route {
#[doc = "# Errors"] #[doc = "# Errors"]
/// If `access_token` is not set. /// If `access_token` is not set.
pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> { pub fn $name(&self, $($param: $typ,)*) -> Result<$ret> {
use std::io::Read;
let form_data = json!({ let form_data = json!({
$( $(
@ -154,7 +135,7 @@ macro_rules! route {
)* )*
}); });
let mut response = self.client.post(&self.route(concat!("/api/v1/", $url))) let response = self.client.post(&self.route(concat!("/api/v1/", $url)))
.headers(self.headers.clone()) .headers(self.headers.clone())
.json(&form_data) .json(&form_data)
.send()?; .send()?;
@ -167,14 +148,7 @@ macro_rules! route {
return Err(Error::Server(status)); return Err(Error::Server(status));
} }
let mut vec = Vec::new(); json_convert_response(response)
response.read_to_end(&mut vec)?;
match json::from_slice(&vec) {
Ok(res) => Ok(res),
Err(_) => Err(Error::Api(json::from_slice(&vec)?)),
}
} }
route!{$($rest)*} route!{$($rest)*}
@ -392,21 +366,13 @@ impl Mastodon {
/// Post a new status to the account. /// Post a new status to the account.
pub fn new_status(&self, status: StatusBuilder) -> Result<Status> { pub fn new_status(&self, status: StatusBuilder) -> Result<Status> {
use std::io::Read;
let mut response = self.client.post(&self.route("/api/v1/statuses")) let response = self.client.post(&self.route("/api/v1/statuses"))
.headers(self.headers.clone()) .headers(self.headers.clone())
.json(&status) .json(&status)
.send()?; .send()?;
let mut vec = Vec::new(); json_convert_response(response)
response.read_to_end(&mut vec)?;
if let Ok(t) = json::from_slice(&vec) {
Ok(t)
} else {
Err(Error::Api(json::from_slice(&vec)?))
}
} }
/// Get the federated timeline for the instance. /// Get the federated timeline for the instance.
@ -526,3 +492,24 @@ from! {
HttpError, Http, HttpError, Http,
IoError, Io, IoError, Io,
} }
// Convert the HTTP response body from JSON. Pass up deserialization errors
// transparently.
fn json_convert_response<T: for<'de> serde::Deserialize<'de>>(mut response: Response) -> Result<T> {
use std::io::Read;
let mut vec = Vec::new();
response.read_to_end(&mut vec)?;
match json::from_slice(&vec) {
Ok(t) => Ok(t),
// If deserializing into the desired type fails try again to
// see if this is an error response.
Err(e) => {
if let Ok(error) = json::from_slice(&vec) {
return Err(Error::Api(error));
}
Err(e.into())
},
}
}

Loading…
Cancel
Save