]> git.proxmox.com Git - pxar.git/blob - src/encoder/sync.rs
d32d8f62b5aa1c0949f96ee3a87a8d5322d64a18
[pxar.git] / src / encoder / sync.rs
1 //! Blocking `pxar` encoder.
2
3 use std::io;
4 use std::path::Path;
5 use std::pin::Pin;
6 use std::task::{Context, Poll};
7
8 use crate::decoder::sync::StandardReader;
9 use crate::encoder::{self, SeqWrite, LinkOffset};
10 use crate::format;
11 use crate::util::poll_result_once;
12 use crate::Metadata;
13
14 /// Blocking `pxar` encoder.
15 ///
16 /// This is the blocking I/O version of the `pxar` encoder. This will *not* work with an
17 /// asynchronous I/O object. I/O must always return `Poll::Ready`.
18 ///
19 /// Attempting to use a `Waker` from this context *will* `panic!`
20 ///
21 /// If you need to use asynchronous I/O, use `aio::Encoder`.
22 #[repr(transparent)]
23 pub struct Encoder<'a, T: SeqWrite + 'a> {
24 inner: encoder::EncoderImpl<'a, T>,
25 }
26
27 impl<'a, T: io::Write + 'a> Encoder<'a, StandardWriter<T>> {
28 /// Encode a `pxar` archive into a regular `std::io::Write` output.
29 #[inline]
30 pub fn from_std(output: T, metadata: &Metadata) -> io::Result<Encoder<StandardWriter<T>>> {
31 Encoder::new(StandardWriter::new(output), metadata)
32 }
33 }
34
35 impl<'a> Encoder<'a, StandardWriter<std::fs::File>> {
36 /// Convenience shortcut for `File::create` followed by `Encoder::from_file`.
37 pub fn create<'b, P: AsRef<Path>>(
38 path: P,
39 metadata: &'b Metadata,
40 ) -> io::Result<Encoder<'a, StandardWriter<std::fs::File>>> {
41 Encoder::new(
42 StandardWriter::new(std::fs::File::create(path.as_ref())?),
43 metadata,
44 )
45 }
46 }
47
48 impl<'a, T: SeqWrite + 'a> Encoder<'a, T> {
49 /// Create a *blocking* encoder for an output implementing our internal write interface.
50 ///
51 /// Note that the `output`'s `SeqWrite` implementation must always return `Poll::Ready` and is
52 /// not allowed to use the `Waker`, as this will cause a `panic!`.
53 pub fn new(output: T, metadata: &Metadata) -> io::Result<Self> {
54 Ok(Self {
55 inner: poll_result_once(encoder::EncoderImpl::new(output, metadata))?,
56 })
57 }
58
59 /// Create a new regular file in the archive. This returns a `File` object to which the
60 /// contents have to be written out *completely*. Failing to do so will put the encoder into an
61 /// error state.
62 pub fn create_file<'b, P: AsRef<Path>>(
63 &'b mut self,
64 metadata: &Metadata,
65 file_name: P,
66 file_size: u64,
67 ) -> io::Result<File<'b>>
68 where
69 'a: 'b,
70 {
71 Ok(File {
72 inner: poll_result_once(self.inner.create_file(
73 metadata,
74 file_name.as_ref(),
75 file_size,
76 ))?,
77 })
78 }
79
80 /// Convenience shortcut to add a *regular* file by path including its contents to the archive.
81 pub fn add_file<P: AsRef<Path>>(
82 &mut self,
83 metadata: &Metadata,
84 file_name: P,
85 file_size: u64,
86 content: &mut dyn io::Read,
87 ) -> io::Result<LinkOffset> {
88 poll_result_once(self.inner.add_file(
89 metadata,
90 file_name.as_ref(),
91 file_size,
92 &mut StandardReader::new(content),
93 ))
94 }
95
96 /// Create a new subdirectory. Note that the subdirectory has to be finished by calling the
97 /// `finish()` method, otherwise the entire archive will be in an error state.
98 pub fn create_directory<'b, P: AsRef<Path>>(
99 &'b mut self,
100 file_name: P,
101 metadata: &Metadata,
102 ) -> io::Result<Encoder<'b, &'b mut dyn SeqWrite>>
103 where
104 'a: 'b,
105 {
106 Ok(Encoder {
107 inner: poll_result_once(self.inner.create_directory(file_name.as_ref(), metadata))?,
108 })
109 }
110
111 /// Finish this directory. This is mandatory, otherwise the `Drop` handler will `panic!`.
112 pub fn finish(self) -> io::Result<()> {
113 poll_result_once(self.inner.finish())
114 }
115
116 /// Add a symbolic link to the archive.
117 pub fn add_symlink<PF: AsRef<Path>, PT: AsRef<Path>>(
118 &mut self,
119 metadata: &Metadata,
120 file_name: PF,
121 target: PT,
122 ) -> io::Result<LinkOffset> {
123 poll_result_once(
124 self.inner
125 .add_symlink(metadata, file_name.as_ref(), target.as_ref()),
126 )
127 }
128
129 /// Add a hard link to the archive.
130 pub fn add_hardlink<PF: AsRef<Path>, PT: AsRef<Path>>(
131 &mut self,
132 file_name: PF,
133 target: PT,
134 offset: LinkOffset,
135 ) -> io::Result<()> {
136 poll_result_once(self.inner.add_hardlink(file_name.as_ref(), target.as_ref(), offset))
137 }
138
139 /// Add a device node to the archive.
140 pub fn add_device<P: AsRef<Path>>(
141 &mut self,
142 metadata: &Metadata,
143 file_name: P,
144 device: format::Device,
145 ) -> io::Result<LinkOffset> {
146 poll_result_once(self.inner.add_device(metadata, file_name.as_ref(), device))
147 }
148
149 /// Add a fifo node to the archive.
150 pub fn add_fifo<P: AsRef<Path>>(
151 &mut self,
152 metadata: &Metadata,
153 file_name: P,
154 ) -> io::Result<LinkOffset> {
155 poll_result_once(self.inner.add_fifo(metadata, file_name.as_ref()))
156 }
157
158 /// Add a socket node to the archive.
159 pub fn add_socket<P: AsRef<Path>>(
160 &mut self,
161 metadata: &Metadata,
162 file_name: P,
163 ) -> io::Result<LinkOffset> {
164 poll_result_once(self.inner.add_socket(metadata, file_name.as_ref()))
165 }
166 }
167
168 #[repr(transparent)]
169 pub struct File<'a> {
170 inner: encoder::FileImpl<'a>,
171 }
172
173 impl<'a> File<'a> {
174 /// Get the file offset to be able to reference it with `add_hardlink`.
175 pub fn file_offset(&self) -> LinkOffset {
176 self.inner.file_offset()
177 }
178 }
179
180 impl<'a> io::Write for File<'a> {
181 fn write(&mut self, data: &[u8]) -> io::Result<usize> {
182 poll_result_once(self.inner.write(data))
183 }
184
185 fn flush(&mut self) -> io::Result<()> {
186 Ok(())
187 }
188 }
189
190 /// Pxar encoder write adapter for `std::io::Write`.
191 pub struct StandardWriter<T> {
192 inner: Option<T>,
193 position: u64,
194 }
195
196 impl<T: io::Write> StandardWriter<T> {
197 pub fn new(inner: T) -> Self {
198 Self {
199 inner: Some(inner),
200 position: 0,
201 }
202 }
203
204 fn inner(&mut self) -> io::Result<&mut T> {
205 self.inner
206 .as_mut()
207 .ok_or_else(|| io_format_err!("write after close"))
208 }
209
210 fn pin_to_inner(self: Pin<&mut Self>) -> io::Result<&mut T> {
211 unsafe { self.get_unchecked_mut() }.inner()
212 }
213 }
214
215 impl<T: io::Write> SeqWrite for StandardWriter<T> {
216 fn poll_seq_write(
217 self: Pin<&mut Self>,
218 _cx: &mut Context,
219 buf: &[u8],
220 ) -> Poll<io::Result<usize>> {
221 let this = unsafe { self.get_unchecked_mut() };
222 Poll::Ready(match this.inner()?.write(buf) {
223 Ok(got) => {
224 this.position += got as u64;
225 Ok(got)
226 }
227 Err(err) => Err(err),
228 })
229 }
230
231 fn poll_position(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<u64>> {
232 Poll::Ready(Ok(self.as_ref().position))
233 }
234
235 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
236 Poll::Ready(self.pin_to_inner().and_then(|inner| inner.flush()))
237 }
238
239 fn poll_close(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
240 let this = unsafe { self.get_unchecked_mut() };
241 Poll::Ready(match this.inner.as_mut() {
242 None => Ok(()),
243 Some(inner) => {
244 inner.flush()?;
245 this.inner = None;
246 Ok(())
247 }
248 })
249 }
250 }