]> git.proxmox.com Git - pxar.git/blame - src/accessor/sync.rs
api update
[pxar.git] / src / accessor / sync.rs
CommitLineData
6cd4f635
WB
1//! Blocking `pxar` random access handling.
2
3use std::io;
4use std::os::unix::fs::FileExt;
5use std::path::Path;
6use std::pin::Pin;
7use std::task::{Context, Poll};
8
9use crate::accessor::{self, ReadAt};
10use crate::decoder::Decoder;
11use crate::util::poll_result_once;
12use crate::Entry;
13
14/// Blocking `pxar` random-access decoder.
15///
16/// This is the blocking I/O version of the `pxar` accessor. 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::Accessor`.
22#[repr(transparent)]
23pub struct Accessor<T> {
24 inner: accessor::AccessorImpl<T>,
25}
26
29c17fc0 27impl<T: FileExt> Accessor<FileReader<T>> {
6cd4f635
WB
28 /// Decode a `pxar` archive from a standard file implementing `FileExt`.
29 #[inline]
29c17fc0 30 pub fn from_file_and_size(input: T, size: u64) -> io::Result<Self> {
6cd4f635
WB
31 Accessor::new(FileReader::new(input), size)
32 }
33}
34
35impl Accessor<FileReader<std::fs::File>> {
36 /// Decode a `pxar` archive from a regular `std::io::File` input.
37 #[inline]
38 pub fn from_file(input: std::fs::File) -> io::Result<Self> {
39 let size = input.metadata()?.len();
40 Accessor::from_file_and_size(input, size)
41 }
42
43 /// Convenience shortcut for `File::open` followed by `Accessor::from_file`.
44 pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
45 Self::from_file(std::fs::File::open(path.as_ref())?)
46 }
47}
48
29c17fc0
WB
49impl<T: Clone + std::ops::Deref<Target = std::fs::File>> Accessor<FileRefReader<T>> {
50 /// Open an `Arc` or `Rc` of `File`.
51 pub fn from_file_ref(input: T) -> io::Result<Self> {
52 let size = input.deref().metadata()?.len();
53 Accessor::from_file_ref_and_size(input, size)
54 }
55}
56
57impl<T> Accessor<FileRefReader<T>>
58where
59 T: Clone + std::ops::Deref,
60 T::Target: FileExt,
61{
62 /// Open an `Arc` or `Rc` of `File`.
63 pub fn from_file_ref_and_size(input: T, size: u64) -> io::Result<Accessor<FileRefReader<T>>> {
64 Accessor::new(FileRefReader::new(input), size)
65 }
66}
67
6cd4f635
WB
68impl<T: ReadAt> Accessor<T> {
69 /// Create a *blocking* random-access decoder from an input implementing our internal read
70 /// interface.
71 ///
72 /// Note that the `input`'s `SeqRead` implementation must always return `Poll::Ready` and is
73 /// not allowed to use the `Waker`, as this will cause a `panic!`.
74 pub fn new(input: T, size: u64) -> io::Result<Self> {
75 Ok(Self {
76 inner: poll_result_once(accessor::AccessorImpl::new(input, size))?,
77 })
78 }
79
80 /// Open a directory handle to the root of the pxar archive.
29c17fc0
WB
81 pub fn open_root_ref<'a>(&'a self) -> io::Result<Directory<&'a dyn ReadAt>> {
82 Ok(Directory::new(poll_result_once(
83 self.inner.open_root_ref(),
84 )?))
85 }
86}
87
88impl<T: Clone + ReadAt> Accessor<T> {
89 pub fn open_root(&self) -> io::Result<Directory<T>> {
6cd4f635
WB
90 Ok(Directory::new(poll_result_once(self.inner.open_root())?))
91 }
92}
93
94/// Adapter for FileExt readers.
29c17fc0 95#[derive(Clone)]
6cd4f635
WB
96pub struct FileReader<T> {
97 inner: T,
98}
99
100impl<T: FileExt> FileReader<T> {
101 pub fn new(inner: T) -> Self {
102 Self { inner }
103 }
104}
105
106impl<T: FileExt> ReadAt for FileReader<T> {
107 fn poll_read_at(
108 self: Pin<&Self>,
109 _cx: &mut Context,
110 buf: &mut [u8],
111 offset: u64,
112 ) -> Poll<io::Result<usize>> {
113 Poll::Ready(self.get_ref().inner.read_at(buf, offset))
114 }
115}
116
29c17fc0
WB
117/// Adapter for `Arc` or `Rc` to FileExt readers.
118#[derive(Clone)]
119pub struct FileRefReader<T: Clone> {
120 inner: T,
121}
122
123impl<T: Clone> FileRefReader<T> {
124 pub fn new(inner: T) -> Self {
125 Self { inner }
126 }
127}
128
129impl<T> ReadAt for FileRefReader<T>
130where
131 T: Clone + std::ops::Deref,
132 T::Target: FileExt,
133{
134 fn poll_read_at(
135 self: Pin<&Self>,
136 _cx: &mut Context,
137 buf: &mut [u8],
138 offset: u64,
139 ) -> Poll<io::Result<usize>> {
140 Poll::Ready(self.get_ref().inner.read_at(buf, offset))
141 }
142}
143
6cd4f635
WB
144/// Blocking Directory variant:
145#[repr(transparent)]
29c17fc0
WB
146pub struct Directory<T> {
147 inner: accessor::DirectoryImpl<T>,
6cd4f635
WB
148}
149
29c17fc0
WB
150impl<T: Clone + ReadAt> Directory<T> {
151 fn new(inner: accessor::DirectoryImpl<T>) -> Self {
6cd4f635
WB
152 Self { inner }
153 }
154
155 /// Get a decoder for the directory contents.
29c17fc0 156 pub fn decode_full(&self) -> io::Result<Decoder<accessor::SeqReadAtAdapter<T>>> {
6cd4f635
WB
157 Ok(Decoder::from_impl(poll_result_once(
158 self.inner.decode_full(),
159 )?))
160 }
161
162 /// Lookup an entry in a directory.
29c17fc0 163 pub fn lookup<P: AsRef<Path>>(&self, path: P) -> io::Result<Option<FileEntry<T>>> {
6cd4f635
WB
164 if let Some(file_entry) = poll_result_once(self.inner.lookup(path.as_ref()))? {
165 Ok(Some(FileEntry { inner: file_entry }))
166 } else {
167 Ok(None)
168 }
169 }
170
171 /// Get an iterator over the directory's contents.
29c17fc0 172 pub fn read_dir<'a>(&'a self) -> ReadDir<'a, T> {
6cd4f635
WB
173 ReadDir {
174 inner: self.inner.read_dir(),
175 }
176 }
177}
178
179/// A file entry retrieved from a `Directory` via the `lookup` method.
180#[repr(transparent)]
29c17fc0
WB
181pub struct FileEntry<T: Clone + ReadAt> {
182 inner: accessor::FileEntryImpl<T>,
6cd4f635
WB
183}
184
29c17fc0
WB
185impl<T: Clone + ReadAt> FileEntry<T> {
186 pub fn enter_directory(&self) -> io::Result<Directory<T>> {
6cd4f635
WB
187 Ok(Directory::new(poll_result_once(
188 self.inner.enter_directory(),
189 )?))
190 }
191
98b894a9
WB
192 pub fn contents(&self) -> io::Result<FileContents<T>> {
193 Ok(FileContents {
194 inner: poll_result_once(self.inner.contents())?,
195 at: 0,
196 })
197 }
198
6cd4f635
WB
199 #[inline]
200 pub fn into_entry(self) -> Entry {
201 self.inner.into_entry()
202 }
203
204 #[inline]
205 pub fn entry(&self) -> &Entry {
206 &self.inner.entry()
207 }
208}
209
29c17fc0 210impl<T: Clone + ReadAt> std::ops::Deref for FileEntry<T> {
05005b14
WB
211 type Target = Entry;
212
213 fn deref(&self) -> &Self::Target {
214 self.entry()
215 }
216}
217
6cd4f635
WB
218/// An iterator over the contents of a `Directory`.
219#[repr(transparent)]
29c17fc0
WB
220pub struct ReadDir<'a, T> {
221 inner: accessor::ReadDirImpl<'a, T>,
6cd4f635
WB
222}
223
98b894a9
WB
224impl<'a, T: Clone + ReadAt> ReadDir<'a, T> {
225 /// Efficient alternative to `Iterator::skip`.
226 #[inline]
227 pub fn skip(self, n: usize) -> Self {
228 Self {
229 inner: self.inner.skip(n),
230 }
231 }
232
233 /// Efficient alternative to `Iterator::count`.
234 #[inline]
235 pub fn count(self) -> usize {
236 self.inner.count()
237 }
238}
239
29c17fc0
WB
240impl<'a, T: Clone + ReadAt> Iterator for ReadDir<'a, T> {
241 type Item = io::Result<DirEntry<'a, T>>;
6cd4f635
WB
242
243 fn next(&mut self) -> Option<Self::Item> {
244 match poll_result_once(self.inner.next()) {
245 Ok(Some(inner)) => Some(Ok(DirEntry { inner })),
246 Ok(None) => None,
247 Err(err) => Some(Err(err)),
248 }
249 }
250}
251
29c17fc0 252impl<'a, T: Clone + ReadAt> std::iter::FusedIterator for ReadDir<'a, T> {}
6cd4f635
WB
253
254/// A directory entry. When iterating through the contents of a directory we first get access to
255/// the file name. The remaining information can be decoded afterwards.
256#[repr(transparent)]
29c17fc0
WB
257pub struct DirEntry<'a, T: Clone + ReadAt> {
258 inner: accessor::DirEntryImpl<'a, T>,
6cd4f635
WB
259}
260
29c17fc0 261impl<'a, T: Clone + ReadAt> DirEntry<'a, T> {
6cd4f635
WB
262 pub fn file_name(&self) -> &Path {
263 self.inner.file_name()
264 }
265
29c17fc0 266 pub fn get_entry(&self) -> io::Result<FileEntry<T>> {
6cd4f635
WB
267 poll_result_once(self.inner.get_entry()).map(|inner| FileEntry { inner })
268 }
269}
98b894a9
WB
270
271/// A reader for file contents.
272pub struct FileContents<T> {
273 inner: accessor::FileContentsImpl<T>,
274 at: u64,
275}
276
277impl<T: Clone + ReadAt> io::Read for FileContents<T> {
278 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
279 let got = poll_result_once(self.inner.read_at(buf, self.at))?;
280 self.at += got as u64;
281 Ok(got)
282 }
283}
284
285impl<T: Clone + ReadAt> FileExt for FileContents<T> {
286 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
287 poll_result_once(self.inner.read_at(buf, offset))
288 }
289
290 fn write_at(&self, _buf: &[u8], _offset: u64) -> io::Result<usize> {
291 io_bail!("write_at on read-only file");
292 }
293}
294
295impl<T: Clone + ReadAt> ReadAt for FileContents<T> {
296 fn poll_read_at(
297 self: Pin<&Self>,
298 _cx: &mut Context,
299 buf: &mut [u8],
300 offset: u64,
301 ) -> Poll<io::Result<usize>> {
302 Poll::Ready(poll_result_once(self.get_ref().inner.read_at(buf, offset)))
303 }
304}