1 //! Contains Tar-specific building and unpacking functions
7 sync::mpsc::{self, Receiver},
18 utils::{self, Bytes, FileVisibilityPolicy},
21 /// Unpacks the archive given by `archive` into the folder given by `into`.
22 /// Assumes that output_folder is empty
23 pub fn unpack_archive(
24 reader: Box<dyn Read>,
26 mut display_handle: impl Write,
27 ) -> crate::Result<Vec<PathBuf>> {
28 assert!(output_folder.read_dir().expect("dir exists").count() == 0);
29 let mut archive = tar::Archive::new(reader);
31 let mut files_unpacked = vec![];
32 for file in archive.entries()? {
35 let file_path = output_folder.join(file.path()?);
36 file.unpack_in(output_folder)?;
38 // This is printed for every file in the archive and has little
39 // importance for most users, but would generate lots of
40 // spoken text for users using screen readers, braille displays
43 info!(@display_handle, inaccessible, "{:?} extracted. ({})", utils::strip_cur_dir(&output_folder.join(file.path()?)), Bytes::new(file.size()));
45 files_unpacked.push(file_path);
51 /// List contents of `archive`, returning a vector of archive entries
53 mut archive: tar::Archive<impl Read + Send + 'static>,
54 ) -> impl Iterator<Item = crate::Result<FileInArchive>> {
55 struct Files(Receiver<crate::Result<FileInArchive>>);
56 impl Iterator for Files {
57 type Item = crate::Result<FileInArchive>;
59 fn next(&mut self) -> Option<Self::Item> {
64 let (tx, rx) = mpsc::channel();
65 thread::spawn(move || {
66 for file in archive.entries().expect("entries is only used once") {
67 let file_in_archive = (|| {
69 let path = file.path()?.into_owned();
70 let is_dir = file.header().entry_type().is_dir();
71 Ok(FileInArchive { path, is_dir })
73 tx.send(file_in_archive).unwrap();
80 /// Compresses the archives given by `input_filenames` into the file given previously to `writer`.
81 pub fn build_archive_from_paths<W, D>(
82 input_filenames: &[PathBuf],
84 file_visibility_policy: FileVisibilityPolicy,
85 mut display_handle: D,
91 let mut builder = tar::Builder::new(writer);
93 for filename in input_filenames {
94 let previous_location = utils::cd_into_same_dir_as(filename)?;
96 // Safe unwrap, input shall be treated before
97 let filename = filename.file_name().unwrap();
99 for entry in file_visibility_policy.build_walker(filename) {
101 let path = entry.path();
103 // This is printed for every file in `input_filenames` and has
104 // little importance for most users, but would generate lots of
105 // spoken text for users using screen readers, braille displays
107 info!(@display_handle, inaccessible, "Compressing '{}'.", utils::to_utf(path));
110 builder.append_dir(path, path)?;
112 let mut file = match fs::File::open(path) {
115 if e.kind() == std::io::ErrorKind::NotFound && utils::is_symlink(path) {
116 // This path is for a broken symlink
120 return Err(e.into());
123 builder.append_file(path, file.file_mut()).map_err(|err| {
124 FinalError::with_title("Could not create archive")
125 .detail("Unexpected error while trying to read file")
126 .detail(format!("Error: {}.", err))
130 env::set_current_dir(previous_location)?;
133 Ok(builder.into_inner()?)