use std::future::Future;
use serde::de::DeserializeOwned;
use crate::client::http::transport::{Accepted, ExchangeResponse, TransportError};
use crate::utils::address::Address;
use crate::utils::json::Json;
pub const INFO: &str = "/info";
pub const EXCHANGE: &str = "/exchange";
pub trait Transport {
fn post(&self, path: &str, body: String) -> impl Future<Output = Result<String, TransportError>>;
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ApiError {
#[error(transparent)]
Transport(#[from] TransportError),
#[error("the venue's answer on {path} could not be read: {detail}")]
Unreadable { path: &'static str, detail: String },
#[error("{0}")]
Refused(String),
}
#[must_use]
pub fn info_request(kind: &str, fields: &[(&str, Json)]) -> String {
let mut object = Vec::with_capacity(fields.len() + 1);
object.push(("type".to_owned(), Json::String(kind.to_owned())));
object.extend(fields.iter().map(|(key, value)| ((*key).to_owned(), value.clone())));
Json::Object(object).compact()
}
#[must_use]
pub fn user_request(kind: &str, user: Address) -> String {
info_request(kind, &[("user", Json::String(user.to_hex()))])
}
pub async fn info<R: DeserializeOwned>(transport: &impl Transport, request: String) -> Result<R, ApiError> {
let text = transport.post(INFO, request).await?;
serde_json::from_str(&text).map_err(|error| ApiError::Unreadable { path: INFO, detail: unreadable(&error, &text) })
}
pub async fn exchange(transport: &impl Transport, body: String) -> Result<Accepted, ApiError> {
let text = transport.post(EXCHANGE, body).await?;
match ExchangeResponse::parse(&text)? {
ExchangeResponse::Ok(accepted) => Ok(accepted),
ExchangeResponse::Err(reason) => Err(ApiError::Refused(reason)),
}
}
pub(super) fn unreadable(error: &serde_json::Error, text: &str) -> String {
let shown: String = text.chars().take(300).collect();
format!("{error} in {shown}")
}
#[cfg(test)]
#[path = "tests/calls.rs"]
mod tests;