1 //! Asynchronous `pxar` format handling.
6 use std
::task
::{Context, Poll}
;
8 use crate::encoder
::{self, LinkOffset, PayloadOffset, SeqWrite}
;
10 use crate::{Metadata, PxarVariant}
;
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(
25 output
: PxarVariant
<T
, T
>,
27 prelude
: Option
<&[u8]>,
28 ) -> io
::Result
<Encoder
<'a
, TokioWriter
<T
>>> {
30 output
.wrap(|output
| TokioWriter
::new(output
)),
38 #[cfg(feature = "tokio-fs")]
39 impl<'a
> Encoder
<'a
, TokioWriter
<tokio
::fs
::File
>> {
40 /// Convenience shortcut for `File::create` followed by `Encoder::from_tokio`.
41 pub async
fn create
<'b
, P
: AsRef
<Path
>>(
43 metadata
: &'b Metadata
,
44 ) -> io
::Result
<Encoder
<'a
, TokioWriter
<tokio
::fs
::File
>>> {
46 PxarVariant
::Unified(TokioWriter
::new(
47 tokio
::fs
::File
::create(path
.as_ref()).await?
,
56 impl<'a
, T
: SeqWrite
+ 'a
> Encoder
<'a
, T
> {
57 /// Create an asynchronous encoder for an output implementing our internal write interface.
59 output
: PxarVariant
<T
, T
>,
61 prelude
: Option
<&[u8]>,
62 ) -> io
::Result
<Encoder
<'a
, T
>> {
63 let output
= output
.wrap_multi(|output
| output
.into(), |payload_output
| payload_output
);
65 inner
: encoder
::EncoderImpl
::new(output
, metadata
, prelude
).await?
,
69 /// Create a new regular file in the archive. This returns a `File` object to which the
70 /// contents have to be written out *completely*. Failing to do so will put the encoder into an
72 pub async
fn create_file
<'b
, P
: AsRef
<Path
>>(
77 ) -> io
::Result
<File
<'b
, T
>>
84 .create_file(metadata
, file_name
.as_ref(), file_size
)
89 /// Get current position for payload stream
90 pub fn payload_position(&self) -> io
::Result
<PayloadOffset
> {
91 self.inner
.payload_position()
94 // /// Convenience shortcut to add a *regular* file by path including its contents to the archive.
95 // pub async fn add_file<P, F>(
97 // metadata: &Metadata,
100 // content: &mut dyn tokio::io::Read,
101 // ) -> io::Result<()>
106 // self.inner.add_file(
108 // file_name.as_ref(),
110 // content.as_async_reader(),
114 /// Encode a payload reference pointing to given offset in the separate payload output
116 /// Returns with error if the encoder instance has no separate payload output or encoding
118 pub async
fn add_payload_ref(
123 payload_offset
: PayloadOffset
,
124 ) -> io
::Result
<LinkOffset
> {
126 .add_payload_ref(metadata
, file_name
.as_ref(), file_size
, payload_offset
)
130 /// Add size to payload stream
131 pub fn advance(&mut self, size
: PayloadOffset
) -> io
::Result
<()> {
132 self.inner
.advance(size
)
135 /// Create a new subdirectory. Note that the subdirectory has to be finished by calling the
136 /// `finish()` method, otherwise the entire archive will be in an error state.
137 pub async
fn create_directory
<P
: AsRef
<Path
>>(
141 ) -> io
::Result
<()> {
143 .create_directory(file_name
.as_ref(), metadata
)
147 /// Finish this directory. This is mandatory, encodes the end for the current directory.
148 pub async
fn finish(&mut self) -> io
::Result
<()> {
149 self.inner
.finish().await
152 /// Close the encoder instance. This is mandatory, encodes the end for the optional payload
153 /// output stream, if some is given
154 pub async
fn close(self) -> io
::Result
<()> {
155 self.inner
.close().await
158 /// Add a symbolic link to the archive.
159 pub async
fn add_symlink
<PF
: AsRef
<Path
>, PT
: AsRef
<Path
>>(
164 ) -> io
::Result
<()> {
166 .add_symlink(metadata
, file_name
.as_ref(), target
.as_ref())
170 /// Add a hard link to the archive.
171 pub async
fn add_hardlink
<PF
: AsRef
<Path
>, PT
: AsRef
<Path
>>(
176 ) -> io
::Result
<()> {
178 .add_hardlink(file_name
.as_ref(), target
.as_ref(), offset
)
182 /// Add a device node to the archive.
183 pub async
fn add_device
<P
: AsRef
<Path
>>(
187 device
: format
::Device
,
188 ) -> io
::Result
<()> {
190 .add_device(metadata
, file_name
.as_ref(), device
)
194 /// Add a device node to the archive.
195 pub async
fn add_fifo
<P
: AsRef
<Path
>>(
199 ) -> io
::Result
<()> {
200 self.inner
.add_fifo(metadata
, file_name
.as_ref()).await
203 /// Add a device node to the archive.
204 pub async
fn add_socket
<P
: AsRef
<Path
>>(
208 ) -> io
::Result
<()> {
209 self.inner
.add_socket(metadata
, file_name
.as_ref()).await
213 /// This is a "file" inside a pxar archive, to which the initially declared amount of data should
216 /// Writing more or less than the designated amount is an error and will cause the produced archive
219 pub struct File
<'a
, S
: SeqWrite
> {
220 inner
: encoder
::FileImpl
<'a
, S
>,
223 impl<'a
, S
: SeqWrite
> File
<'a
, S
> {
224 /// Get the file offset to be able to reference it with `add_hardlink`.
225 pub fn file_offset(&self) -> LinkOffset
{
226 self.inner
.file_offset()
229 /// Write file data for the current file entry in a pxar archive.
230 pub async
fn write(&mut self, data
: &[u8]) -> io
::Result
<usize> {
231 self.inner
.write(data
).await
234 /// Completely write file data for the current file entry in a pxar archive.
235 pub async
fn write_all(&mut self, data
: &[u8]) -> io
::Result
<()> {
236 self.inner
.write_all(data
).await
240 #[cfg(feature = "tokio-io")]
241 impl<'a
, S
: SeqWrite
> tokio
::io
::AsyncWrite
for File
<'a
, S
> {
242 fn poll_write(self: Pin
<&mut Self>, cx
: &mut Context
, data
: &[u8]) -> Poll
<io
::Result
<usize>> {
243 unsafe { self.map_unchecked_mut(|this| &mut this.inner) }
.poll_write(cx
, data
)
246 fn poll_flush(self: Pin
<&mut Self>, cx
: &mut Context
) -> Poll
<io
::Result
<()>> {
247 unsafe { self.map_unchecked_mut(|this| &mut this.inner) }
.poll_flush(cx
)
250 fn poll_shutdown(self: Pin
<&mut Self>, cx
: &mut Context
) -> Poll
<io
::Result
<()>> {
251 unsafe { self.map_unchecked_mut(|this| &mut this.inner) }
.poll_close(cx
)
255 /// Pxar encoder write adapter for `tokio::io::AsyncWrite`.
256 #[cfg(feature = "tokio-io")]
260 use std
::task
::{Context, Poll}
;
262 use crate::encoder
::SeqWrite
;
264 /// Pxar encoder write adapter for [`AsyncWrite`](tokio::io::AsyncWrite).
265 pub struct TokioWriter
<T
> {
269 impl<T
: tokio
::io
::AsyncWrite
> TokioWriter
<T
> {
270 /// Make a new [`SeqWrite`] wrapper for an object implementing
271 /// [`AsyncWrite`](tokio::io::AsyncWrite).
272 pub fn new(inner
: T
) -> Self {
273 Self { inner: Some(inner) }
276 fn inner_mut(&mut self) -> io
::Result
<Pin
<&mut T
>> {
280 .ok_or_else(|| io_format_err
!("write after close"))?
;
281 Ok(unsafe { Pin::new_unchecked(inner) }
)
284 fn inner(self: Pin
<&mut Self>) -> io
::Result
<Pin
<&mut T
>> {
285 unsafe { self.get_unchecked_mut() }
.inner_mut()
289 impl<T
: tokio
::io
::AsyncWrite
> SeqWrite
for TokioWriter
<T
> {
291 self: Pin
<&mut Self>,
294 ) -> Poll
<io
::Result
<usize>> {
295 let this
= unsafe { self.get_unchecked_mut() }
;
296 this
.inner_mut()?
.poll_write(cx
, buf
)
299 fn poll_flush(self: Pin
<&mut Self>, cx
: &mut Context
) -> Poll
<io
::Result
<()>> {
300 self.inner()?
.poll_flush(cx
)
305 #[cfg(feature = "tokio-io")]
306 pub use tokio_writer
::TokioWriter
;
312 use std
::task
::{Context, Poll}
;
319 impl super::SeqWrite
for DummyOutput
{
321 self: Pin
<&mut Self>,
324 ) -> Poll
<io
::Result
<usize>> {
328 fn poll_flush(self: Pin
<&mut Self>, _cx
: &mut Context
) -> Poll
<io
::Result
<()>> {
334 /// Assert that `Encoder` is `Send`
337 let mut encoder
= Encoder
::new(
338 crate::PxarVariant
::Unified(DummyOutput
),
339 &Metadata
::dir_builder(0o700).build(),
346 .create_directory("baba", &Metadata
::dir_builder(0o700).build())
350 .create_file(&Metadata
::file_builder(0o755).build(), "abab", 1024)
354 encoder
.finish().await
.unwrap();
357 fn test_send
<T
: Send
>(_
: T
) {}