use std::collections::HashSet;
use std::fmt;
use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
use http::Method;
use super::{HttpTransport, HttpTransportResult};
use crate::request::{PreparedRequest, RequestId};
#[derive(Default)]
struct FakeState {
routes: Vec<(Method, String, HttpTransportResult)>,
submitted: Vec<(RequestId, PreparedRequest)>,
answered: HashSet<RequestId>,
outbox: Vec<(RequestId, HttpTransportResult)>,
cancelled: Vec<RequestId>,
shutdowns: usize,
}
#[derive(Clone, Default)]
pub struct FakeHttpTransport {
state: Arc<Mutex<FakeState>>,
}
impl fmt::Debug for FakeHttpTransport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let state = self.lock();
f.debug_struct("FakeHttpTransport")
.field("routes", &state.routes.len())
.field("submitted", &state.submitted.len())
.field("queued_answers", &state.outbox.len())
.finish()
}
}
impl FakeHttpTransport {
pub fn new() -> Self {
Self::default()
}
fn lock(&self) -> MutexGuard<'_, FakeState> {
self.state.lock().unwrap_or_else(PoisonError::into_inner)
}
pub fn route(&self, method: Method, path: &str, result: HttpTransportResult) -> &Self {
self.lock().routes.push((method, path.to_string(), result));
self
}
pub fn clear_routes(&self) {
self.lock().routes.clear();
}
pub fn reply(&self, id: RequestId, result: HttpTransportResult) {
let mut state = self.lock();
state.answered.insert(id);
state.outbox.push((id, result));
}
pub fn requests(&self) -> Vec<(RequestId, PreparedRequest)> {
self.lock().submitted.clone()
}
pub fn last_request(&self) -> Option<(RequestId, PreparedRequest)> {
self.lock().submitted.last().cloned()
}
pub fn waiting(&self) -> Vec<RequestId> {
let state = self.lock();
state.submitted.iter().map(|(id, _)| *id).filter(|id| !state.answered.contains(id)).collect()
}
pub fn cancelled(&self) -> Vec<RequestId> {
self.lock().cancelled.clone()
}
pub fn shutdown_count(&self) -> usize {
self.lock().shutdowns
}
}
impl HttpTransport for FakeHttpTransport {
fn submit(&mut self, id: RequestId, request: PreparedRequest) {
let mut state = self.lock();
let route = state.routes.iter().rev().find(|(method, path, _)| *method == request.method && path == request.path()).map(|(_, _, r)| r.clone());
if let Some(result) = route {
state.answered.insert(id);
state.outbox.push((id, result));
}
state.submitted.push((id, request));
}
fn poll(&mut self) -> Vec<(RequestId, HttpTransportResult)> {
std::mem::take(&mut self.lock().outbox)
}
fn cancel(&mut self, id: RequestId) {
self.lock().cancelled.push(id);
}
fn shutdown(&mut self) {
self.lock().shutdowns += 1;
}
}