anyreader_walker/
entry.rs

1use crate::stack::AnyWalker;
2use crate::walkers::{ArchiveVisitor, FileWalker, TarWalker, ZipWalker};
3use anyreader::AnyFormat;
4use anyreader::FormatKind;
5use bytes::buf::Reader;
6use bytes::{Buf, Bytes};
7use std::fmt::{Debug, Display, Formatter};
8use std::fs::File;
9use std::io::{BufReader, Read};
10use std::path::{Path, PathBuf};
11
12/// Represents the details of a [FileEntry], including its path and size.
13#[derive(Debug, Clone, Eq, PartialEq)]
14pub struct EntryDetails {
15    pub path: PathBuf,
16    pub size: u64,
17}
18
19impl EntryDetails {
20    pub fn new(path: impl Into<PathBuf>, size: u64) -> Self {
21        Self {
22            path: path.into(),
23            size,
24        }
25    }
26}
27
28impl Display for EntryDetails {
29    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
30        write!(f, "{} ({} bytes)", self.path.display(), self.size)
31    }
32}
33
34/// A [FileEntry] represents a file in an archive, along with its format and size.
35/// It can be used to read the file's contents, and can also be used to visit the contents of
36/// an archive.
37///
38/// # Example
39/// This walks a nested tar file
40/// ```
41/// # use std::io::Read;
42/// use std::path::{PathBuf, Path};
43/// # use anyreader::test::{tar_archive, zip_archive};
44/// use anyreader_walker::{FileEntry, AnyWalker, EntryDetails, FormatKind, ArchiveStack};
45/// // Create a tar archive containing a nested tar archive, containing a nested zip archive
46/// let tar_archive = tar_archive([
47///     ("test", b"Hello, world!".to_vec()),
48///     ("nested.tar", tar_archive([
49///         ("nested", b"Hello, nested!".to_vec()),
50///         ("nested2", b"Hello, nested2!".to_vec()),
51///         ("nested_zip", zip_archive([("nested3", "Hello, nested zip!")]))
52///     ])),
53/// ]);
54/// let path = Path::new("archive.tar.gz");
55/// let entry = FileEntry::from_bytes(&path, tar_archive).unwrap();
56///
57/// #[derive(Default)]
58/// struct Visitor {
59///    names: Vec<PathBuf>,
60///    stack: ArchiveStack
61/// }
62///
63/// impl AnyWalker for Visitor {
64///     fn visit_file_entry(&mut self, entry: &mut FileEntry<impl Read>) -> std::io::Result<()> {
65///         self.names.push(self.stack.full_path().join(&entry.path()));
66///         Ok(())
67///     }
68///
69///     fn begin_visit_archive(&mut self, details: &EntryDetails, format: FormatKind) -> std::io::Result<bool> {
70///         self.stack.push_details(details.clone());
71///         Ok(true)
72///     }
73///     fn end_visit_archive(&mut self, details: EntryDetails, format: FormatKind) -> std::io::Result<()> {
74///        self.stack.pop_details();
75///        Ok(())
76///    }
77/// }
78///
79/// let mut visitor = Visitor::default();
80/// visitor.walk(entry).unwrap();
81///
82/// assert_eq!(visitor.names, [
83///     path.join("test"),
84///     path.join("nested.tar").join("nested"),
85///     path.join("nested.tar").join("nested2"),
86///     path.join("nested.tar").join("nested_zip").join("nested3"),
87/// ]);
88/// ```
89pub struct FileEntry<T: Read> {
90    details: EntryDetails,
91    inner: AnyFormat<T>,
92}
93
94impl FileEntry<BufReader<File>> {
95    pub fn from_path(path: impl AsRef<Path>) -> std::io::Result<Self> {
96        let file = File::open(&path)?;
97        let size = file.metadata()?.len();
98        let format = AnyFormat::from_reader(BufReader::new(file))?;
99        Ok(Self::new(path.as_ref().to_path_buf(), size, format))
100    }
101}
102
103impl FileEntry<Reader<Bytes>> {
104    pub fn from_bytes(
105        path: impl AsRef<Path>,
106        data: impl Into<Bytes>,
107    ) -> std::io::Result<FileEntry<Reader<Bytes>>> {
108        let data = data.into();
109        let size = data.len() as u64;
110        let inner = AnyFormat::from_reader(data.reader())?;
111        Ok(FileEntry {
112            details: EntryDetails::new(path.as_ref(), size),
113            inner,
114        })
115    }
116}
117
118impl<T: Read> FileEntry<T> {
119    pub fn new(path: PathBuf, size: u64, format: AnyFormat<T>) -> Self {
120        Self {
121            details: EntryDetails::new(path, size),
122            inner: format,
123        }
124    }
125
126    pub fn from_reader(
127        path: impl Into<PathBuf>,
128        size: u64,
129        reader: T,
130    ) -> std::io::Result<FileEntry<T>> {
131        let inner = AnyFormat::from_reader(reader)?;
132        Ok(FileEntry {
133            details: EntryDetails::new(path, size),
134            inner,
135        })
136    }
137
138    pub fn into_components(self) -> (EntryDetails, AnyFormat<T>) {
139        (self.details, self.inner)
140    }
141
142    pub fn details(&self) -> &EntryDetails {
143        &self.details
144    }
145
146    pub fn path(&self) -> &Path {
147        &self.details.path
148    }
149
150    pub fn size(&self) -> u64 {
151        self.details.size
152    }
153
154    pub fn supports_recursion(&self) -> bool {
155        matches!(self.inner.kind, FormatKind::Tar | FormatKind::Zip)
156    }
157
158    pub fn format(&self) -> FormatKind {
159        self.inner.kind
160    }
161
162    pub fn get_ref(&self) -> &T {
163        self.inner.get_ref()
164    }
165}
166
167impl<'a, T: Read + 'a> ArchiveVisitor<'a> for FileEntry<T> {
168    type Item = T;
169
170    #[inline(always)]
171    fn visit<V: AnyWalker>(mut self, visitor: &mut V) -> std::io::Result<()> {
172        match self.format() {
173            FormatKind::Tar => TarWalker::new(&mut self as &mut dyn Read).visit(visitor),
174            FormatKind::Zip => ZipWalker::new(&mut self as &mut dyn Read).visit(visitor),
175            _ => FileWalker::new(self).visit(visitor),
176        }
177    }
178}
179
180impl<T: Read> Debug for FileEntry<T> {
181    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
182        f.debug_struct("ArchiveEntry")
183            .field("path", &self.details.path)
184            .field("size", &self.details.size)
185            .field("format", &self.inner)
186            .finish()
187    }
188}
189
190impl<T: Read> Read for FileEntry<T> {
191    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
192        self.inner.read(buf)
193    }
194}