]>
Commit | Line | Data |
---|---|---|
70acf637 WB |
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; | |
d6748862 | 9 | use crate::encoder::{self, SeqWrite, LinkOffset}; |
07b244df | 10 | use crate::format; |
70acf637 WB |
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> { | |
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)] | |
169 | pub struct File<'a> { | |
170 | inner: encoder::FileImpl<'a>, | |
171 | } | |
172 | ||
d6748862 WB |
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 | ||
70acf637 WB |
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> { | |
05356884 | 192 | inner: Option<T>, |
70acf637 WB |
193 | position: u64, |
194 | } | |
195 | ||
196 | impl<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 | ||
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() }; | |
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 | } |