1#![doc = include_str!("../README.md")]
2
3use std::io;
4use std::io::Read;
5use std::path::{Path, PathBuf};
6
7mod container;
8mod peekable;
9mod stream;
10
11pub use crate::container::{ArchiveKind, Container, ContainerKind, Items};
12pub use crate::stream::CompressionKind;
13pub use crate::stream::StreamKind;
14
15#[derive(Debug, strum::EnumIs)]
16pub enum FileKind {
17 File,
18 Directory,
19 Other,
20}
21
22#[derive(Debug)]
23pub struct FileItem<T: Read> {
24 pub path: PathBuf,
25 pub reader: T,
26 pub kind: FileKind,
27}
28
29pub fn recursive_read<F>(path: &Path, mut reader: impl Read, callback: &mut F) -> io::Result<()>
30where
31 F: FnMut(FileItem<&mut dyn Read>) -> io::Result<()>,
32{
33 read_recursive_inner(path, FileKind::File, &mut reader as &mut dyn Read, callback)?;
34 Ok(())
35}
36
37fn handle_container<F>(path: &Path, mut archive: impl Container, callback: &mut F) -> io::Result<()>
38where
39 F: FnMut(FileItem<&mut dyn Read>) -> io::Result<()>,
40{
41 let mut items = archive.items()?;
42 while let Some(x) = items.next_item() {
43 let mut x = x?;
44 let reader = &mut x.reader as &mut dyn Read;
45 read_recursive_inner(path.join(x.path).as_path(), x.kind, reader, callback)?;
46 }
47 Ok(())
48}
49
50fn read_recursive_inner<F>(
51 path: &Path,
52 kind: FileKind,
53 reader: &mut dyn Read,
54 callback: &mut F,
55) -> io::Result<()>
56where
57 F: FnMut(FileItem<&mut dyn Read>) -> io::Result<()>,
58{
59 let container = ContainerKind::from_reader(reader)?;
60 match container {
61 ContainerKind::Stream(StreamKind::Raw(mut r)) => callback(FileItem {
62 path: path.to_path_buf(),
63 reader: &mut r as &mut dyn Read,
64 kind,
65 }),
66 ContainerKind::Stream(StreamKind::Compressed(mut c)) => {
67 read_recursive_inner(path, kind, &mut c as &mut dyn Read, callback)
68 }
69 ContainerKind::Archive(ArchiveKind::Tar(r)) => handle_container(path, r, callback),
70 ContainerKind::Archive(ArchiveKind::Zip(r)) => handle_container(path, r, callback),
71 }
72}