use crate::error::IoError;
use crate::transport::{StartTlsCapable, Transport};
use core::future::Future;
use core::pin::pin;
use core::task::{Context, Poll, Waker};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
#[derive(Debug, Clone)]
pub enum UpgradeBehavior {
Succeed,
Fail(&'static str),
}
pub fn block_on<F: Future>(fut: F) -> F::Output {
let waker = Waker::noop();
let mut cx = Context::from_waker(waker);
let mut fut = pin!(fut);
match fut.as_mut().poll(&mut cx) {
Poll::Ready(value) => value,
Poll::Pending => panic!("mock-driven future returned Pending"),
}
}
pub type MockHandles = (MockTransport, Rc<RefCell<Vec<u8>>>, Rc<RefCell<bool>>);
pub type MockStartTlsHandles = (
MockTransport,
Rc<RefCell<Vec<u8>>>,
Rc<RefCell<bool>>,
Rc<RefCell<u32>>,
);
pub struct MockTransport {
incoming: VecDeque<Vec<u8>>,
pending_post: VecDeque<Vec<u8>>,
written: Rc<RefCell<Vec<u8>>>,
closed: Rc<RefCell<bool>>,
upgrades: Rc<RefCell<u32>>,
upgrade_behavior: UpgradeBehavior,
}
impl MockTransport {
pub fn new(chunks: &[&[u8]]) -> MockHandles {
let (t, w, c, _u) = Self::build(chunks, &[], UpgradeBehavior::Succeed);
(t, w, c)
}
pub fn with_starttls(
pre_chunks: &[&[u8]],
post_chunks: &[&[u8]],
behavior: UpgradeBehavior,
) -> MockStartTlsHandles {
Self::build(pre_chunks, post_chunks, behavior)
}
fn build(
pre_chunks: &[&[u8]],
post_chunks: &[&[u8]],
behavior: UpgradeBehavior,
) -> MockStartTlsHandles {
let written = Rc::new(RefCell::new(Vec::new()));
let closed = Rc::new(RefCell::new(false));
let upgrades = Rc::new(RefCell::new(0u32));
let mut q: VecDeque<Vec<u8>> = VecDeque::new();
for c in pre_chunks {
q.push_back((*c).to_vec());
}
let mut pending_post: VecDeque<Vec<u8>> = VecDeque::new();
for c in post_chunks {
pending_post.push_back((*c).to_vec());
}
(
Self {
incoming: q,
pending_post,
written: Rc::clone(&written),
closed: Rc::clone(&closed),
upgrades: Rc::clone(&upgrades),
upgrade_behavior: behavior,
},
written,
closed,
upgrades,
)
}
}
impl Transport for MockTransport {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
let Some(chunk) = self.incoming.front_mut() else {
return Ok(0);
};
let n = buf.len().min(chunk.len());
buf[..n].copy_from_slice(&chunk[..n]);
chunk.drain(..n);
if chunk.is_empty() {
self.incoming.pop_front();
}
Ok(n)
}
async fn write_all(&mut self, buf: &[u8]) -> Result<(), IoError> {
self.written.borrow_mut().extend_from_slice(buf);
Ok(())
}
async fn close(&mut self) -> Result<(), IoError> {
*self.closed.borrow_mut() = true;
Ok(())
}
}
impl StartTlsCapable for MockTransport {
async fn upgrade_to_tls(&mut self) -> Result<(), IoError> {
*self.upgrades.borrow_mut() += 1;
match &self.upgrade_behavior {
UpgradeBehavior::Succeed => {
while let Some(chunk) = self.pending_post.pop_front() {
self.incoming.push_back(chunk);
}
Ok(())
}
UpgradeBehavior::Fail(msg) => Err(IoError::new(*msg)),
}
}
}
pub fn flatten(parts: &[&[u8]]) -> Vec<u8> {
let mut v = Vec::new();
for p in parts {
v.extend_from_slice(p);
}
v
}