use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
use crate::canonical::CanonicalError;
use crate::protocol::WireRequest;
use crate::transport::{Transport, TransportResponse};
#[derive(Clone)]
pub enum Chunk {
Data(Vec<u8>),
Fail(io::ErrorKind),
}
pub struct MockTransport {
status: u16,
chunks: Vec<Chunk>,
retry_after: Option<String>,
seen: Mutex<Vec<WireRequest>>,
}
impl MockTransport {
pub fn new(status: u16, chunks: Vec<Chunk>) -> Self {
MockTransport {
status,
chunks,
retry_after: None,
seen: Mutex::new(Vec::new()),
}
}
pub fn ok(chunks: Vec<&[u8]>) -> Self {
let chunks = chunks
.into_iter()
.map(|c| Chunk::Data(c.to_vec()))
.collect();
MockTransport::new(200, chunks)
}
pub fn with_retry_after(mut self, header: impl Into<String>) -> Self {
self.retry_after = Some(header.into());
self
}
pub fn requests(&self) -> Vec<WireRequest> {
self.seen
.lock()
.ok()
.map(|s| s.to_vec())
.unwrap_or_default()
}
}
impl Transport for MockTransport {
fn send(&self, wire: WireRequest) -> Result<TransportResponse, CanonicalError> {
if let Ok(mut seen) = self.seen.lock() {
seen.push(wire);
}
let body = self.chunks.clone().into_iter().map(|c| match c {
Chunk::Data(bytes) => Ok(bytes),
Chunk::Fail(kind) => Err(io::Error::from(kind)),
});
Ok(TransportResponse {
status: self.status,
body: Box::new(body),
retry_after: self.retry_after.clone(),
})
}
}
pub struct ScriptedTransport {
responses: Vec<(u16, Vec<u8>)>,
next: AtomicUsize,
seen: Mutex<Vec<WireRequest>>,
}
impl ScriptedTransport {
pub fn new(responses: Vec<(u16, Vec<u8>)>) -> Self {
ScriptedTransport {
responses,
next: AtomicUsize::new(0),
seen: Mutex::new(Vec::new()),
}
}
pub fn requests(&self) -> Vec<WireRequest> {
self.seen
.lock()
.ok()
.map(|s| s.to_vec())
.unwrap_or_default()
}
}
impl Transport for ScriptedTransport {
fn send(&self, wire: WireRequest) -> Result<TransportResponse, CanonicalError> {
if let Ok(mut seen) = self.seen.lock() {
seen.push(wire);
}
let n = self.next.fetch_add(1, Ordering::Relaxed);
let idx = n.min(self.responses.len().saturating_sub(1));
let (status, body) = self.responses[idx].clone();
Ok(TransportResponse {
status,
body: Box::new(std::iter::once(Ok(body))),
retry_after: None,
})
}
}