use std::collections::VecDeque;
use std::convert::Infallible;
use std::sync::{Mutex, PoisonError};
use http::{Request, Response, StatusCode, header};
pub type HttpRequest = Request<Vec<u8>>;
pub type HttpResponse = Response<Vec<u8>>;
pub trait SyncClient {
type Error: std::error::Error + Send + Sync + 'static;
fn send(&self, request: HttpRequest) -> Result<HttpResponse, Self::Error>;
}
pub trait AsyncClient {
type Error: std::error::Error + Send + Sync + 'static;
fn send(
&self,
request: HttpRequest,
) -> impl Future<Output = Result<HttpResponse, Self::Error>> + Send;
}
#[derive(Debug, Default)]
pub struct Recorder {
answers: Mutex<VecDeque<HttpResponse>>,
sent: Mutex<Vec<HttpRequest>>,
}
impl Recorder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn answering(self, status: StatusCode, body: &serde_json::Value) -> Self {
self.answers
.lock()
.unwrap_or_else(PoisonError::into_inner)
.push_back(json_response(status, body));
self
}
pub fn take(&self) -> Vec<HttpRequest> {
std::mem::take(&mut self.sent.lock().unwrap_or_else(PoisonError::into_inner))
}
fn answer(&self, request: HttpRequest) -> HttpResponse {
self.sent
.lock()
.unwrap_or_else(PoisonError::into_inner)
.push(request);
self.answers
.lock()
.unwrap_or_else(PoisonError::into_inner)
.pop_front()
.unwrap_or_else(|| json_response(StatusCode::OK, &serde_json::json!({})))
}
}
impl SyncClient for Recorder {
type Error = Infallible;
fn send(&self, request: HttpRequest) -> Result<HttpResponse, Infallible> {
Ok(self.answer(request))
}
}
impl AsyncClient for Recorder {
type Error = Infallible;
fn send(
&self,
request: HttpRequest,
) -> impl Future<Output = Result<HttpResponse, Infallible>> + Send {
std::future::ready(Ok(self.answer(request)))
}
}
fn json_response(status: StatusCode, body: &serde_json::Value) -> HttpResponse {
let mut response = Response::new(body.to_string().into_bytes());
*response.status_mut() = status;
response.headers_mut().insert(
header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"),
);
response
}