pin_cursor/
lib.rs

1//! A simple `!Unpin` I/O backend for async-std, designed for use in tests.
2//!
3//! This crate provides the `PinCursor` struct which wraps around `async_std::io::Cursor`
4//! but is explicitly **not** `Unpin`. It is a little building block to help write tests
5//! where you want to ensure that your own asynchronous IO code behaves correctly when reading from
6//! or writing to something that is *definitely* `!Unpin`.
7//!
8//! - It can be backed by any `Unpin` data buffer that can be slotted into `async_std::io::Cursor`.
9//!   Usually `Vec<u8>` or `&mut [u8]` (e. g. from an array) are used.
10//! - It implements `async_std::io::{Read, Write, Seek}`, so you can poll these traits' methods
11//!   in your own futures.
12//! - At the same time, it provides several high-level methods through which you can manipulate
13//!   the PinCursor in a simple `async {}` block.
14//!
15//! # Examples
16//!
17//! ```
18//! # use async_std::task::block_on;
19//! use pin_cursor::PinCursor;
20//! use async_std::io::{prelude::*, Cursor};
21//! use std::io::SeekFrom;
22//! use std::pin::Pin;
23//!
24//! // Construct a async_std::io::Cursor however you like...
25//! let mut data: Vec<u8> = Vec::new();
26//! let cursor = Cursor::new(&mut data);
27//! // ... then wrap it in PinCursor and a pinned pointer, thus losing the Unpin privileges.
28//! let mut cursor: Pin<Box<PinCursor<_>>> = Box::pin(PinCursor::wrap(cursor));
29//! // Note that we have to make an owning pointer first -
30//! // making a Pin<&mut PinCursor<_>> directly is impossible!
31//! // (There is a more complex way to allocate on stack - see the features section.)
32//!
33//! // Methods of PinCursor mostly return futures and are designed for use in async contexts.
34//! # block_on(
35//! async {
36//!     // You can write!
37//!     assert_eq!(cursor.as_mut().write(&[1u8, 2u8, 3u8]).await.unwrap(), 3);
38//! 
39//!     // You can seek!
40//!     assert_eq!(cursor.position(), 3);
41//!     assert_eq!(cursor.as_mut().seek(SeekFrom::Start(1)).await.unwrap(), 1);
42//!     assert_eq!(cursor.position(), 1);
43//! 
44//!     // You can read!
45//!     let mut buf = [0u8; 1];
46//!     assert_eq!(cursor.as_mut().read(buf.as_mut()).await.unwrap(), 1);
47//!     assert_eq!(buf[0], 2);
48//!
49//!     // There's also this way of seeking that doesn't involve futures.
50//!     cursor.as_mut().set_position(0);
51//!     assert_eq!(cursor.as_mut().read(buf.as_mut()).await.unwrap(), 1);
52//!     assert_eq!(buf[0], 1);
53//! }
54//! # );
55//! ```
56//!
57//! # Features
58//!
59//! The optional feature `stackpin` enables integration with [stackpin], a crate that provides
60//! a way to allocate `!Unpin` structures on stack.
61//!
62//! ```ignore
63//! # use pin_cursor::PinCursor;
64//! # use async_std::io::Cursor;
65//! # use std::pin::Pin;
66//! use stackpin::stack_let;
67//!
68//! let mut data: Vec<u8> = vec![1, 2];
69//! stack_let!(mut cursor : PinCursor<_> = Cursor::new(&mut data));
70//! let cursor_ptr: Pin<&mut PinCursor<_>> = Pin::as_mut(&mut cursor);
71//! ```
72//!
73//! Now you have a correctly pinned `PinCursor` that's allocated on stack instead of in a box.
74//!
75//! [stackpin]: https://docs.rs/stackpin/0.0.2
76
77use std::future::Future;
78use std::io::{IoSlice, IoSliceMut, Result, SeekFrom};
79use std::marker::PhantomPinned;
80use std::pin::Pin;
81use std::task::{Context, Poll};
82
83use async_std::io::Cursor;
84use async_std::io::prelude::*;
85use pin_project_lite::pin_project;
86
87#[cfg(feature = "stackpin")]
88mod impl_stackpin;
89
90pin_project! {
91    pub struct PinCursor<T> {
92        c: Cursor<T>,
93        #[pin]
94        _p: PhantomPinned
95    }
96}
97
98impl<T> PinCursor<T>
99    where T: Unpin,
100          Cursor<T>: Write + Read + Seek
101{
102    pub fn wrap(c: Cursor<T>) -> Self {
103        Self { c, _p: PhantomPinned }
104    }
105
106    pub fn unwrap(self) -> Cursor<T> {
107        self.c
108    }
109
110    pub fn position(&self) -> u64 {
111        self.c.position()
112    }
113
114    pub fn set_position(self: Pin<&mut Self>, pos: u64) {
115        self.project().c.set_position(pos)
116    }
117
118    pub fn write<'a>(self: Pin<&'a mut Self>, buf: &'a [u8]) -> impl Future<Output=Result<usize>> + 'a {
119        self.project().c.write(buf)
120    }
121
122    pub fn read<'a>(self: Pin<&'a mut Self>, buf: &'a mut [u8]) -> impl Future<Output=Result<usize>> + 'a {
123        self.project().c.read(buf)
124    }
125
126    pub fn seek(self: Pin<&mut Self>, pos: SeekFrom) -> impl Future<Output=Result<u64>> + '_ {
127        self.project().c.seek(pos)
128    }
129}
130
131impl<T> Read for PinCursor<T>
132    where T: Unpin,
133          Cursor<T>: Read
134{
135    fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>> {
136        Pin::new(self.project().c).poll_read(cx, buf)
137    }
138
139    fn poll_read_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>]) -> Poll<Result<usize>> {
140        Pin::new(self.project().c).poll_read_vectored(cx, bufs)
141    }
142}
143
144impl<T> Write for PinCursor<T>
145    where T: Unpin,
146          Cursor<T>: Write
147{
148    fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
149        Pin::new(self.project().c).poll_write(cx, buf)
150    }
151
152    fn poll_write_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>]) -> Poll<Result<usize>> {
153        Pin::new(self.project().c).poll_write_vectored(cx, bufs)
154    }
155
156    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
157        Pin::new(self.project().c).poll_flush(cx)
158    }
159
160    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
161        Pin::new(self.project().c).poll_close(cx)
162    }
163}
164
165impl<T> Seek for PinCursor<T>
166    where T: Unpin,
167          Cursor<T>: Seek
168{
169    fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) -> Poll<Result<u64>> {
170        Pin::new(self.project().c).poll_seek(cx, pos)
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use static_assertions::{assert_impl_all, assert_not_impl_all};
177
178    use super::*;
179
180    #[test]
181    fn impls() {
182        assert_not_impl_all!(PinCursor<Vec<u8>>: Unpin);
183        assert_impl_all!(PinCursor<Vec<u8>>: Read, Write, Seek);
184    }
185}