diff options
author | Jesse Luehrs <doy@tozt.net> | 2020-05-23 16:16:25 -0400 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2020-05-23 16:16:25 -0400 |
commit | 80acb66445a612798ddf74bd6df60b51fd674fa0 (patch) | |
tree | 9c6b5fe2cb6bf33c79523a2ab2dcd9d0124c3e32 | |
parent | c9473e0b61bc9d8264a6ce939ed74c3d9b8f4db5 (diff) | |
download | rbw-80acb66445a612798ddf74bd6df60b51fd674fa0.tar.gz rbw-80acb66445a612798ddf74bd6df60b51fd674fa0.zip |
better error messages when parsing a server message
-rw-r--r-- | Cargo.lock | 22 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/api.rs | 26 | ||||
-rw-r--r-- | src/error.rs | 5 | ||||
-rw-r--r-- | src/json.rs | 38 | ||||
-rw-r--r-- | src/lib.rs | 1 |
6 files changed, 80 insertions, 14 deletions
@@ -84,6 +84,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] +name = "async-trait" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1110,6 +1121,7 @@ dependencies = [ "aes", "anyhow", "arrayvec", + "async-trait", "base64 0.12.1", "block-modes", "chbs", @@ -1129,6 +1141,7 @@ dependencies = [ "ring", "serde", "serde_json", + "serde_path_to_error", "snafu", "structopt", "tempfile", @@ -1333,6 +1346,15 @@ dependencies = [ ] [[package]] +name = "serde_path_to_error" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "359b895005d818163c78a24d272cc98567cce80c2461cf73f513da1d296c0b62" +dependencies = [ + "serde", +] + +[[package]] name = "serde_urlencoded" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -15,6 +15,7 @@ license = "MIT" aes = "0.3" anyhow = "1.0" arrayvec = "0.5" +async-trait = "0.1" base64 = "0.12" block-modes = "0.3" chbs = "0.0.10" @@ -34,6 +35,7 @@ reqwest = { version = "0.10", features = ["blocking", "json"] } ring = "0.16" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +serde_path_to_error = "0.1" snafu = "0.6" structopt = { version = "0.3", features = ["paw"] } tempfile = "3.1" @@ -1,5 +1,9 @@ use crate::prelude::*; +use crate::json::{ + DeserializeJsonWithPath as _, DeserializeJsonWithPathAsync as _, +}; + #[derive(serde::Serialize, Debug)] struct PreloginReq { email: String, @@ -385,8 +389,7 @@ impl Client { .send() .await .context(crate::error::Reqwest)?; - let prelogin_res: PreloginRes = - res.json().await.context(crate::error::Reqwest)?; + let prelogin_res: PreloginRes = res.json_with_path().await?; Ok(prelogin_res.kdf_iterations) } @@ -417,7 +420,7 @@ impl Client { .context(crate::error::Reqwest)?; if let reqwest::StatusCode::OK = res.status() { let connect_res: ConnectPasswordRes = - res.json().await.context(crate::error::Reqwest)?; + res.json_with_path().await?; Ok(( connect_res.access_token, connect_res.refresh_token, @@ -425,8 +428,7 @@ impl Client { )) } else { let code = res.status().as_u16(); - let error_res: ConnectErrorRes = - res.json().await.context(crate::error::Reqwest)?; + let error_res: ConnectErrorRes = res.json_with_path().await?; if error_res.error_model.message == "Username or password is incorrect. Try again" { @@ -455,8 +457,7 @@ impl Client { .context(crate::error::Reqwest)?; match res.status() { reqwest::StatusCode::OK => { - let sync_res: SyncRes = - res.json().await.context(crate::error::Reqwest)?; + let sync_res: SyncRes = res.json_with_path().await?; let folders = sync_res.folders.clone(); let ciphers = sync_res .ciphers @@ -760,8 +761,7 @@ impl Client { .context(crate::error::Reqwest)?; match res.status() { reqwest::StatusCode::OK => { - let folders_res: FoldersRes = - res.json().context(crate::error::Reqwest)?; + let folders_res: FoldersRes = res.json_with_path()?; Ok(folders_res .data .iter() @@ -794,8 +794,7 @@ impl Client { .context(crate::error::Reqwest)?; match res.status() { reqwest::StatusCode::OK => { - let folders_res: FoldersResData = - res.json().context(crate::error::Reqwest)?; + let folders_res: FoldersResData = res.json_with_path()?; Ok(folders_res.id) } reqwest::StatusCode::UNAUTHORIZED => { @@ -822,8 +821,7 @@ impl Client { .form(&connect_req) .send() .context(crate::error::Reqwest)?; - let connect_res: ConnectRefreshTokenRes = - res.json().context(crate::error::Reqwest)?; + let connect_res: ConnectRefreshTokenRes = res.json_with_path()?; Ok(connect_res.access_token) } @@ -844,7 +842,7 @@ impl Client { .await .context(crate::error::Reqwest)?; let connect_res: ConnectRefreshTokenRes = - res.json().await.context(crate::error::Reqwest)?; + res.json_with_path().await?; Ok(connect_res.access_token) } diff --git a/src/error.rs b/src/error.rs index a6e6a84..6e8a51e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -43,6 +43,11 @@ pub enum Error { #[snafu(display("invalid mac"))] InvalidMac, + #[snafu(display("{}", source))] + JSON { + source: serde_path_to_error::Error<serde_json::Error>, + }, + #[snafu(display("failed to load config: {}", source))] LoadConfig { source: std::io::Error }, diff --git a/src/json.rs b/src/json.rs new file mode 100644 index 0000000..2816a1f --- /dev/null +++ b/src/json.rs @@ -0,0 +1,38 @@ +use crate::prelude::*; + +pub trait DeserializeJsonWithPath { + fn json_with_path<T: serde::de::DeserializeOwned>(self) -> Result<T>; +} + +impl DeserializeJsonWithPath for String { + fn json_with_path<T: serde::de::DeserializeOwned>(self) -> Result<T> { + let jd = &mut serde_json::Deserializer::from_str(&self); + serde_path_to_error::deserialize(jd).context(crate::error::JSON) + } +} + +impl DeserializeJsonWithPath for reqwest::blocking::Response { + fn json_with_path<T: serde::de::DeserializeOwned>(self) -> Result<T> { + let bytes = self.bytes().context(crate::error::Reqwest)?; + let jd = &mut serde_json::Deserializer::from_slice(&bytes); + serde_path_to_error::deserialize(jd).context(crate::error::JSON) + } +} + +#[async_trait::async_trait] +pub trait DeserializeJsonWithPathAsync { + async fn json_with_path<T: serde::de::DeserializeOwned>( + self, + ) -> Result<T>; +} + +#[async_trait::async_trait] +impl DeserializeJsonWithPathAsync for reqwest::Response { + async fn json_with_path<T: serde::de::DeserializeOwned>( + self, + ) -> Result<T> { + let bytes = self.bytes().await.context(crate::error::Reqwest)?; + let jd = &mut serde_json::Deserializer::from_slice(&bytes); + serde_path_to_error::deserialize(jd).context(crate::error::JSON) + } +} @@ -19,6 +19,7 @@ pub mod dirs; pub mod edit; pub mod error; pub mod identity; +pub mod json; pub mod locked; pub mod pinentry; mod prelude; |