use serde_json::Value;
use crate::client::http::transport::calls::unreadable;
use crate::client::http::transport::{ApiError, EXCHANGE};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExchangeResponse {
Ok(Accepted),
Err(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Accepted {
pub kind: String,
pub statuses: Vec<Status>,
pub data: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Status {
Ok(Value),
Error(String),
}
impl Accepted {
pub fn errors(&self) -> impl Iterator<Item = &str> {
self.statuses.iter().filter_map(|status| match status {
Status::Error(reason) => Some(reason.as_str()),
Status::Ok(_) => None,
})
}
}
impl ExchangeResponse {
pub fn parse(text: &str) -> Result<ExchangeResponse, ApiError> {
let bad = |detail: String| ApiError::Unreadable { path: EXCHANGE, detail };
let answer: Value = serde_json::from_str(text).map_err(|error| bad(unreadable(&error, text)))?;
let response = answer.get("response").cloned().unwrap_or(Value::Null);
match answer.get("status").and_then(Value::as_str) {
Some("ok") => {
let kind = response.get("type").and_then(Value::as_str).unwrap_or_default().to_owned();
let data = response.get("data");
let statuses = match (
data.and_then(|data| data.get("statuses")).and_then(Value::as_array),
data.and_then(|data| data.get("status")),
) {
(Some(many), _) => many.iter().map(status_of).collect(),
(None, Some(one)) => vec![status_of(one)],
(None, None) => Vec::new(),
};
Ok(ExchangeResponse::Ok(Accepted { kind, statuses, data: data.cloned() }))
}
Some(_) => Ok(ExchangeResponse::Err(text_of(&response))),
None => Err(bad(format!("no status in {}", text.chars().take(300).collect::<String>()))),
}
}
}
fn status_of(status: &Value) -> Status {
match status.get("error") {
Some(error) => Status::Error(text_of(error)),
None => Status::Ok(status.clone()),
}
}
fn text_of(value: &Value) -> String {
value.as_str().map_or_else(|| value.to_string(), ToOwned::to_owned)
}
#[cfg(test)]
#[path = "tests/answer.rs"]
mod tests;