]> git.proxmox.com Git - pxar.git/blob - src/encoder/sync.rs
fix hardlink format
[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};
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<()> {
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<()> {
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 ) -> io::Result<()> {
135 poll_result_once(self.inner.add_hardlink(file_name.as_ref(), target.as_ref()))
136 }
137
138 /// Add a device node to the archive.
139 pub fn add_device<P: AsRef<Path>>(
140 &mut self,
141 metadata: &Metadata,
142 file_name: P,
143 device: format::Device,
144 ) -> io::Result<()> {
145 poll_result_once(self.inner.add_device(metadata, file_name.as_ref(), device))
146 }
147
148 /// Add a fifo node to the archive.
149 pub fn add_fifo<P: AsRef<Path>>(
150 &mut self,
151 metadata: &Metadata,
152 file_name: P,
153 ) -> io::Result<()> {
154 poll_result_once(self.inner.add_fifo(metadata, file_name.as_ref()))
155 }
156
157 /// Add a socket node to the archive.
158 pub fn add_socket<P: AsRef<Path>>(
159 &mut self,
160 metadata: &Metadata,
161 file_name: P,
162 ) -> io::Result<()> {
163 poll_result_once(self.inner.add_socket(metadata, file_name.as_ref()))
164 }
165 }
166
167 #[repr(transparent)]
168 pub struct File<'a> {
169 inner: encoder::FileImpl<'a>,
170 }
171
172 impl<'a> io::Write for File<'a> {
173 fn write(&mut self, data: &[u8]) -> io::Result<usize> {
174 poll_result_once(self.inner.write(data))
175 }
176
177 fn flush(&mut self) -> io::Result<()> {
178 Ok(())
179 }
180 }
181
182 /// Pxar encoder write adapter for `std::io::Write`.
183 pub struct StandardWriter<T> {
184 inner: Option<T>,
185 position: u64,
186 }
187
188 impl<T: io::Write> StandardWriter<T> {
189 pub fn new(inner: T) -> Self {
190 Self {
191 inner: Some(inner),
192 position: 0,
193 }
194 }
195
196 fn inner(&mut self) -> io::Result<&mut T> {
197 self.inner
198 .as_mut()
199 .ok_or_else(|| io_format_err!("write after close"))
200 }
201
202 fn pin_to_inner(self: Pin<&mut Self>) -> io::Result<&mut T> {
203 unsafe { self.get_unchecked_mut() }.inner()
204 }
205 }
206
207 impl<T: io::Write> SeqWrite for StandardWriter<T> {
208 fn poll_seq_write(
209 self: Pin<&mut Self>,
210 _cx: &mut Context,
211 buf: &[u8],
212 ) -> Poll<io::Result<usize>> {
213 let this = unsafe { self.get_unchecked_mut() };
214 Poll::Ready(match this.inner()?.write(buf) {
215 Ok(got) => {
216 this.position += got as u64;
217 Ok(got)
218 }
219 Err(err) => Err(err),
220 })
221 }
222
223 fn poll_position(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<u64>> {
224 Poll::Ready(Ok(self.as_ref().position))
225 }
226
227 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
228 Poll::Ready(self.pin_to_inner().and_then(|inner| inner.flush()))
229 }
230
231 fn poll_close(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
232 let this = unsafe { self.get_unchecked_mut() };
233 Poll::Ready(match this.inner.as_mut() {
234 None => Ok(()),
235 Some(inner) => {
236 inner.flush()?;
237 this.inner = None;
238 Ok(())
239 }
240 })
241 }
242 }