]> git.proxmox.com Git - pxar.git/blame - src/encoder/sync.rs
encoder: introduce LinkOffset
[pxar.git] / src / encoder / sync.rs
CommitLineData
70acf637
WB
1//! Blocking `pxar` encoder.
2
3use std::io;
4use std::path::Path;
5use std::pin::Pin;
6use std::task::{Context, Poll};
7
8use crate::decoder::sync::StandardReader;
d6748862 9use crate::encoder::{self, SeqWrite, LinkOffset};
07b244df 10use crate::format;
70acf637
WB
11use crate::util::poll_result_once;
12use 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)]
23pub struct Encoder<'a, T: SeqWrite + 'a> {
24 inner: encoder::EncoderImpl<'a, T>,
25}
26
27impl<'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
35impl<'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
48impl<'a, T: SeqWrite + 'a> Encoder<'a, T> {
05356884 49 /// Create a *blocking* encoder for an output implementing our internal write interface.
70acf637
WB
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
54109840
WB
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
70acf637 61 /// error state.
cbe8bf57
WB
62 pub fn create_file<'b, P: AsRef<Path>>(
63 &'b mut self,
70acf637
WB
64 metadata: &Metadata,
65 file_name: P,
66 file_size: u64,
cbe8bf57
WB
67 ) -> io::Result<File<'b>>
68 where
69 'a: 'b,
70 {
70acf637
WB
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,
d6748862 87 ) -> io::Result<LinkOffset> {
70acf637
WB
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.
cbe8bf57
WB
98 pub fn create_directory<'b, P: AsRef<Path>>(
99 &'b mut self,
70acf637
WB
100 file_name: P,
101 metadata: &Metadata,
cbe8bf57
WB
102 ) -> io::Result<Encoder<'b, &'b mut dyn SeqWrite>>
103 where
104 'a: 'b,
105 {
70acf637
WB
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 }
07b244df
WB
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,
d6748862 122 ) -> io::Result<LinkOffset> {
07b244df
WB
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,
07b244df
WB
132 file_name: PF,
133 target: PT,
d6748862 134 offset: LinkOffset,
07b244df 135 ) -> io::Result<()> {
b0752929 136 poll_result_once(self.inner.add_hardlink(file_name.as_ref(), target.as_ref(), offset))
07b244df
WB
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,
d6748862 145 ) -> io::Result<LinkOffset> {
07b244df
WB
146 poll_result_once(self.inner.add_device(metadata, file_name.as_ref(), device))
147 }
a7149b09
WB
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,
d6748862 154 ) -> io::Result<LinkOffset> {
a7149b09
WB
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,
d6748862 163 ) -> io::Result<LinkOffset> {
a7149b09
WB
164 poll_result_once(self.inner.add_socket(metadata, file_name.as_ref()))
165 }
70acf637
WB
166}
167
168#[repr(transparent)]
169pub struct File<'a> {
170 inner: encoder::FileImpl<'a>,
171}
172
d6748862
WB
173impl<'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
70acf637
WB
180impl<'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`.
191pub struct StandardWriter<T> {
05356884 192 inner: Option<T>,
70acf637
WB
193 position: u64,
194}
195
196impl<T: io::Write> StandardWriter<T> {
197 pub fn new(inner: T) -> Self {
07b244df
WB
198 Self {
199 inner: Some(inner),
200 position: 0,
201 }
05356884
WB
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()
70acf637
WB
212 }
213}
214
215impl<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() };
05356884 222 Poll::Ready(match this.inner()?.write(buf) {
70acf637
WB
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 }
05356884
WB
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(()),
54109840
WB
243 Some(inner) => {
244 inner.flush()?;
245 this.inner = None;
246 Ok(())
247 }
05356884
WB
248 })
249 }
70acf637 250}