1 //! Asynchronous `pxar` format handling.
6 use std
::task
::{Context, Poll}
;
8 use crate::encoder
::{self, LinkOffset, SeqWrite}
;
12 /// Asynchronous `pxar` encoder.
14 /// This is the `async` version of the `pxar` encoder.
16 pub struct Encoder
<'a
, T
: SeqWrite
+ 'a
> {
17 inner
: encoder
::EncoderImpl
<'a
, T
>,
20 #[cfg(feature = "tokio-io")]
21 impl<'a
, T
: tokio
::io
::AsyncWrite
+ 'a
> Encoder
<'a
, TokioWriter
<T
>> {
22 /// Encode a `pxar` archive into a `tokio::io::AsyncWrite` output.
24 pub async
fn from_tokio(
27 ) -> io
::Result
<Encoder
<'a
, TokioWriter
<T
>>> {
28 Encoder
::new(TokioWriter
::new(output
), metadata
).await
32 #[cfg(feature = "tokio-fs")]
33 impl<'a
> Encoder
<'a
, TokioWriter
<tokio
::fs
::File
>> {
34 /// Convenience shortcut for `File::create` followed by `Encoder::from_tokio`.
35 pub async
fn create
<'b
, P
: AsRef
<Path
>>(
37 metadata
: &'b Metadata
,
38 ) -> io
::Result
<Encoder
<'a
, TokioWriter
<tokio
::fs
::File
>>> {
40 TokioWriter
::new(tokio
::fs
::File
::create(path
.as_ref()).await?
),
47 impl<'a
, T
: SeqWrite
+ 'a
> Encoder
<'a
, T
> {
48 /// Create an asynchronous encoder for an output implementing our internal write interface.
49 pub async
fn new(output
: T
, metadata
: &Metadata
) -> io
::Result
<Encoder
<'a
, T
>> {
51 inner
: encoder
::EncoderImpl
::new(output
.into(), metadata
).await?
,
55 /// Create a new regular file in the archive. This returns a `File` object to which the
56 /// contents have to be written out *completely*. Failing to do so will put the encoder into an
58 pub async
fn create_file
<'b
, P
: AsRef
<Path
>>(
63 ) -> io
::Result
<File
<'b
, T
>>
70 .create_file(metadata
, file_name
.as_ref(), file_size
)
75 // /// Convenience shortcut to add a *regular* file by path including its contents to the archive.
76 // pub async fn add_file<P, F>(
78 // metadata: &Metadata,
81 // content: &mut dyn tokio::io::Read,
82 // ) -> io::Result<()>
87 // self.inner.add_file(
89 // file_name.as_ref(),
91 // content.as_async_reader(),
95 /// Create a new subdirectory. Note that the subdirectory has to be finished by calling the
96 /// `finish()` method, otherwise the entire archive will be in an error state.
97 pub async
fn create_directory
<P
: AsRef
<Path
>>(
101 ) -> io
::Result
<Encoder
<'_
, T
>> {
105 .create_directory(file_name
.as_ref(), metadata
)
110 /// Finish this directory. This is mandatory, otherwise the `Drop` handler will `panic!`.
111 pub async
fn finish(self) -> io
::Result
<()> {
112 self.inner
.finish().await
115 /// Add a symbolic link to the archive.
116 pub async
fn add_symlink
<PF
: AsRef
<Path
>, PT
: AsRef
<Path
>>(
121 ) -> io
::Result
<()> {
123 .add_symlink(metadata
, file_name
.as_ref(), target
.as_ref())
127 /// Add a hard link to the archive.
128 pub async
fn add_hardlink
<PF
: AsRef
<Path
>, PT
: AsRef
<Path
>>(
133 ) -> io
::Result
<()> {
135 .add_hardlink(file_name
.as_ref(), target
.as_ref(), offset
)
139 /// Add a device node to the archive.
140 pub async
fn add_device
<P
: AsRef
<Path
>>(
144 device
: format
::Device
,
145 ) -> io
::Result
<()> {
147 .add_device(metadata
, file_name
.as_ref(), device
)
151 /// Add a device node to the archive.
152 pub async
fn add_fifo
<P
: AsRef
<Path
>>(
156 ) -> io
::Result
<()> {
157 self.inner
.add_fifo(metadata
, file_name
.as_ref()).await
160 /// Add a device node to the archive.
161 pub async
fn add_socket
<P
: AsRef
<Path
>>(
165 ) -> io
::Result
<()> {
166 self.inner
.add_socket(metadata
, file_name
.as_ref()).await
170 /// This is a "file" inside a pxar archive, to which the initially declared amount of data should
173 /// Writing more or less than the designated amount is an error and will cause the produced archive
176 pub struct File
<'a
, S
: SeqWrite
> {
177 inner
: encoder
::FileImpl
<'a
, S
>,
180 impl<'a
, S
: SeqWrite
> File
<'a
, S
> {
181 /// Get the file offset to be able to reference it with `add_hardlink`.
182 pub fn file_offset(&self) -> LinkOffset
{
183 self.inner
.file_offset()
186 /// Write file data for the current file entry in a pxar archive.
187 pub async
fn write(&mut self, data
: &[u8]) -> io
::Result
<usize> {
188 self.inner
.write(data
).await
191 /// Completely write file data for the current file entry in a pxar archive.
192 pub async
fn write_all(&mut self, data
: &[u8]) -> io
::Result
<()> {
193 self.inner
.write_all(data
).await
197 #[cfg(feature = "tokio-io")]
198 impl<'a
, S
: SeqWrite
> tokio
::io
::AsyncWrite
for File
<'a
, S
> {
199 fn poll_write(self: Pin
<&mut Self>, cx
: &mut Context
, data
: &[u8]) -> Poll
<io
::Result
<usize>> {
200 unsafe { self.map_unchecked_mut(|this| &mut this.inner) }
.poll_write(cx
, data
)
203 fn poll_flush(self: Pin
<&mut Self>, cx
: &mut Context
) -> Poll
<io
::Result
<()>> {
204 unsafe { self.map_unchecked_mut(|this| &mut this.inner) }
.poll_flush(cx
)
207 fn poll_shutdown(self: Pin
<&mut Self>, cx
: &mut Context
) -> Poll
<io
::Result
<()>> {
208 unsafe { self.map_unchecked_mut(|this| &mut this.inner) }
.poll_close(cx
)
212 /// Pxar encoder write adapter for `tokio::io::AsyncWrite`.
213 #[cfg(feature = "tokio-io")]
217 use std
::task
::{Context, Poll}
;
219 use crate::encoder
::SeqWrite
;
221 /// Pxar encoder write adapter for [`AsyncWrite`](tokio::io::AsyncWrite).
222 pub struct TokioWriter
<T
> {
226 impl<T
: tokio
::io
::AsyncWrite
> TokioWriter
<T
> {
227 /// Make a new [`SeqWrite`] wrapper for an object implementing
228 /// [`AsyncWrite`](tokio::io::AsyncWrite).
229 pub fn new(inner
: T
) -> Self {
230 Self { inner: Some(inner) }
233 fn inner_mut(&mut self) -> io
::Result
<Pin
<&mut T
>> {
237 .ok_or_else(|| io_format_err
!("write after close"))?
;
238 Ok(unsafe { Pin::new_unchecked(inner) }
)
241 fn inner(self: Pin
<&mut Self>) -> io
::Result
<Pin
<&mut T
>> {
242 unsafe { self.get_unchecked_mut() }
.inner_mut()
246 impl<T
: tokio
::io
::AsyncWrite
> SeqWrite
for TokioWriter
<T
> {
248 self: Pin
<&mut Self>,
251 ) -> Poll
<io
::Result
<usize>> {
252 let this
= unsafe { self.get_unchecked_mut() }
;
253 this
.inner_mut()?
.poll_write(cx
, buf
)
256 fn poll_flush(self: Pin
<&mut Self>, cx
: &mut Context
) -> Poll
<io
::Result
<()>> {
257 self.inner()?
.poll_flush(cx
)
262 #[cfg(feature = "tokio-io")]
263 pub use tokio_writer
::TokioWriter
;
269 use std
::task
::{Context, Poll}
;
276 impl super::SeqWrite
for DummyOutput
{
278 self: Pin
<&mut Self>,
281 ) -> Poll
<io
::Result
<usize>> {
285 fn poll_flush(self: Pin
<&mut Self>, _cx
: &mut Context
) -> Poll
<io
::Result
<()>> {
291 /// Assert that `Encoder` is `Send`
294 let mut encoder
= Encoder
::new(DummyOutput
, &Metadata
::dir_builder(0o700).build())
298 let mut dir
= encoder
299 .create_directory("baba", &Metadata
::dir_builder(0o700).build())
302 dir
.create_file(&Metadata
::file_builder(0o755).build(), "abab", 1024)
306 encoder
.finish().await
.unwrap();
309 fn test_send
<T
: Send
>(_
: T
) {}