use crate::io::{AsyncWrite, AsyncWriteExt};
use std::io;
use std::marker::PhantomData;
pub struct WritePermit<'a, W: ?Sized> {
writer: &'a mut W,
data: Option<Vec<u8>>,
_marker: PhantomData<&'a mut W>,
}
impl<'a, W> WritePermit<'a, W>
where
W: AsyncWrite + Unpin + ?Sized,
{
#[inline]
pub fn new(writer: &'a mut W) -> Self {
Self {
writer,
data: Some(Vec::new()),
_marker: PhantomData,
}
}
#[inline]
pub fn with_capacity(writer: &'a mut W, capacity: usize) -> Self {
Self {
writer,
data: Some(Vec::with_capacity(capacity)),
_marker: PhantomData,
}
}
pub fn stage(&mut self, data: &[u8]) {
if let Some(ref mut buf) = self.data {
buf.extend_from_slice(data);
}
}
#[inline]
#[must_use]
pub fn staged_len(&self) -> usize {
self.data.as_ref().map_or(0, Vec::len)
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.as_ref().is_none_or(Vec::is_empty)
}
pub fn clear(&mut self) {
if let Some(ref mut buf) = self.data {
buf.clear();
}
}
pub async fn commit(mut self) -> io::Result<()> {
if let Some(data) = self.data.take() {
if !data.is_empty() {
self.writer.write_all(&data).await?;
}
}
Ok(())
}
#[inline]
pub fn abort(self) {
drop(self);
}
}
impl<W: ?Sized> Drop for WritePermit<'_, W> {
fn drop(&mut self) {
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll, Waker};
fn noop_waker() -> Waker {
std::task::Waker::noop().clone()
}
fn poll_ready<F: Future>(mut fut: Pin<&mut F>) -> F::Output {
let waker = noop_waker();
let mut cx = Context::from_waker(&waker);
for _ in 0..32 {
if let Poll::Ready(output) = fut.as_mut().poll(&mut cx) {
return output;
}
}
panic!("future did not resolve"); }
fn init_test(name: &str) {
crate::test_utils::init_test_logging();
crate::test_phase!(name);
}
#[test]
fn commit_writes_data() {
init_test("commit_writes_data");
let mut output = Vec::new();
let result = {
let mut permit = WritePermit::new(&mut output);
permit.stage(b"hello ");
permit.stage(b"world");
let staged_len = permit.staged_len();
crate::assert_with_log!(staged_len == 11, "staged_len", 11, staged_len);
let empty = permit.is_empty();
crate::assert_with_log!(!empty, "not empty", false, empty);
let mut fut = Box::pin(permit.commit());
poll_ready(fut.as_mut())
};
let ok = result.is_ok();
crate::assert_with_log!(ok, "commit ok", true, ok);
crate::assert_with_log!(output == b"hello world", "output", b"hello world", output);
crate::test_complete!("commit_writes_data");
}
#[test]
fn abort_discards_data() {
init_test("abort_discards_data");
let mut output = Vec::new();
{
let mut permit = WritePermit::new(&mut output);
permit.stage(b"this should be discarded");
permit.abort();
}
let empty = output.is_empty();
crate::assert_with_log!(empty, "output empty", true, empty);
crate::test_complete!("abort_discards_data");
}
#[test]
fn drop_discards_data() {
init_test("drop_discards_data");
let mut output = Vec::new();
{
let mut permit = WritePermit::new(&mut output);
permit.stage(b"this should be discarded");
}
let empty = output.is_empty();
crate::assert_with_log!(empty, "output empty", true, empty);
crate::test_complete!("drop_discards_data");
}
#[test]
fn clear_removes_staged_data() {
init_test("clear_removes_staged_data");
let mut output = Vec::new();
let result = {
let mut permit = WritePermit::new(&mut output);
permit.stage(b"hello");
let staged_len = permit.staged_len();
crate::assert_with_log!(staged_len == 5, "staged_len", 5, staged_len);
permit.clear();
let empty = permit.is_empty();
crate::assert_with_log!(empty, "empty", true, empty);
let staged_len = permit.staged_len();
crate::assert_with_log!(staged_len == 0, "staged_len", 0, staged_len);
let mut fut = Box::pin(permit.commit());
poll_ready(fut.as_mut())
};
let ok = result.is_ok();
crate::assert_with_log!(ok, "commit ok", true, ok);
let empty = output.is_empty();
crate::assert_with_log!(empty, "output empty", true, empty);
crate::test_complete!("clear_removes_staged_data");
}
#[test]
fn with_capacity_preallocates() {
init_test("with_capacity_preallocates");
let mut output = Vec::new();
let permit = WritePermit::with_capacity(&mut output, 1024);
let empty = permit.is_empty();
crate::assert_with_log!(empty, "empty", true, empty);
crate::test_complete!("with_capacity_preallocates");
}
}