use serde::Deserialize;
use crate::error::Error;
#[derive(Debug, Deserialize)]
pub(crate) struct Envelope<T> {
pub code: i32,
#[serde(default = "Option::default")]
pub data: Option<T>,
#[serde(default)]
pub message: Option<String>,
#[serde(default)]
pub suggestion: Option<String>,
}
impl<T> Envelope<T> {
pub(crate) fn into_result(self) -> Result<T, Error> {
if self.code == 0 {
self.data.ok_or(Error::Api {
code: 0,
message: "missing `data` field in success envelope".into(),
suggestion: None,
})
} else {
Err(Error::Api {
code: self.code,
message: self.message.unwrap_or_else(|| "no message".into()),
suggestion: self.suggestion,
})
}
}
}
pub(crate) fn map_http_error(status: reqwest::StatusCode, bytes: &[u8]) -> Error {
if let Ok(env) = serde_json::from_slice::<Envelope<serde_json::Value>>(bytes)
&& env.code != 0
{
return Error::Api {
code: env.code,
message: env.message.unwrap_or_else(|| status.to_string()),
suggestion: env.suggestion,
};
}
Error::Http {
status: status.as_u16(),
message: String::from_utf8_lossy(bytes).into_owned(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_success_envelope() {
let json = r#"{"code":0,"data":{"balance":12.5,"frozen":0.0}}"#;
let env: Envelope<serde_json::Value> = serde_json::from_str(json).unwrap();
let data = env.into_result().unwrap();
assert_eq!(data["balance"], 12.5);
}
#[test]
fn parses_error_envelope() {
let json = r#"{"code":1001,"message":"bad key","suggestion":"regenerate"}"#;
let env: Envelope<serde_json::Value> = serde_json::from_str(json).unwrap();
let err = env.into_result().unwrap_err();
let crate::Error::Api {
code,
message,
suggestion,
} = err
else {
panic!("wrong variant");
};
assert_eq!(code, 1001);
assert_eq!(message, "bad key");
assert_eq!(suggestion.as_deref(), Some("regenerate"));
}
#[test]
fn error_envelope_without_suggestion() {
let json = r#"{"code":500,"message":"boom"}"#;
let env: Envelope<serde_json::Value> = serde_json::from_str(json).unwrap();
let err = env.into_result().unwrap_err();
assert!(matches!(err, crate::Error::Api { code: 500, .. }));
}
}