1use crate::cli::hidden::HiddenKind;
2use crate::config::Config;
3use crate::error::MyResult;
4use crate::fs::entry::{Entry, EntryResult};
5use crate::fs::system::FileSystem;
6use crate::zip::manager::PasswordManager;
7use crate::zip::parent::ZipParent;
8use crate::zip::{pkzip, seven, tar};
9use std::path::{Component, Path, PathBuf};
10use walkdir::DirEntry;
11
12#[derive(Debug, PartialEq)]
13pub enum ZipKind {
14 PkZip,
15 SevenZ,
16 Tar(bool),
17}
18
19impl ZipKind {
20 pub fn from_path(path: &Path, zip_expand: bool) -> Option<Self> {
21 if zip_expand {
22 if let Some(ext) = path.extension() {
23 let ext = ext.to_ascii_lowercase();
24 if ext == "zip" || ext == "jar" {
25 return Some(Self::PkZip);
26 }
27 if ext == "7z" {
28 return Some(Self::SevenZ);
29 }
30 if ext == "tar" {
31 return Some(Self::Tar(false));
32 }
33 if ext == "gz" {
34 let path = path.with_extension("");
35 if let Some(ext) = path.extension() {
36 let ext = ext.to_ascii_lowercase();
37 if ext == "tar" {
38 return Some(Self::Tar(true));
39 }
40 }
41 }
42 }
43 }
44 None
45 }
46
47 pub fn walk_entries<F: Fn(EntryResult)>(
48 &self,
49 config: &Config,
50 dir_entry: &DirEntry,
51 zip_manager: &mut PasswordManager,
52 function: &F,
53 ) -> MyResult<()> {
54 let parent_path = PathBuf::from(dir_entry.path());
55 let parent_depth = dir_entry.depth();
56 match ZipParent::new(parent_path, parent_depth) {
57 Ok(zip_parent) => {
58 self.walk_inner(config, &zip_parent, zip_manager, &|entry| {
59 match entry {
60 Ok(entry) if filter_entry(config, entry) => function(Ok(entry)),
61 Ok(_) => (),
62 Err(error) => function(Err(error)),
63 }
64 })
65 }
66 Err(error) => {
67 function(Err(error));
68 Ok(())
69 }
70 }
71 }
72
73 fn walk_inner<F: Fn(EntryResult)>(
74 &self,
75 config: &Config,
76 zip_parent: &ZipParent,
77 zip_manager: &mut PasswordManager,
78 function: &F,
79 ) -> MyResult<()> {
80 match self {
81 Self::PkZip => pkzip::walk_entries(zip_parent, zip_manager, config.want_decrypt(), function),
82 Self::SevenZ => seven::walk_entries(zip_parent, zip_manager, config.want_decrypt(), function),
83 Self::Tar(want_gzip) => tar::walk_entries(zip_parent, *want_gzip, function),
84 }
85 }
86}
87
88fn filter_entry(config: &Config, entry: &dyn Entry) -> bool {
89 if let Some(max_depth) = config.max_depth() {
90 if entry.file_depth() > max_depth {
91 return false;
92 }
93 }
94 if let Some(inner_path) = entry.inner_path() {
95 match config.show_hidden() {
96 HiddenKind::None => {
97 return filter_hidden_path(inner_path);
98 }
99 HiddenKind::Files => {
100 if let Some(parent) = inner_path.parent() {
101 return filter_hidden_path(parent);
102 }
103 }
104 HiddenKind::Recurse => {
105 return true;
106 }
107 }
108 }
109 true
110}
111
112fn filter_hidden_path(path: &Path) -> bool {
113 path.components().all(filter_hidden_name)
114}
115
116fn filter_hidden_name(component: Component) -> bool {
117 match component {
118 Component::Normal(name) => !FileSystem::is_hidden_name(name.to_str()),
119 _ => true,
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use crate::zip::wrapper::ZipKind;
126 use googletest::prelude::*;
127 use std::path::PathBuf;
128
129 #[gtest]
130 fn test_file_has_zip_kind() {
131 expect_that!(test_zip_kind("lower", false), none());
132 expect_that!(test_zip_kind("UPPER", false), none());
133 expect_that!(test_zip_kind("lower.zip", false), none());
134 expect_that!(test_zip_kind("UPPER.ZIP", false), none());
135 expect_that!(test_zip_kind("lower.jar", false), none());
136 expect_that!(test_zip_kind("UPPER.JAR", false), none());
137 expect_that!(test_zip_kind("lower.7z", false), none());
138 expect_that!(test_zip_kind("UPPER.7Z", false), none());
139 expect_that!(test_zip_kind("lower.tar", false), none());
140 expect_that!(test_zip_kind("UPPER.TAR", false), none());
141 expect_that!(test_zip_kind("lower.gz", false), none());
142 expect_that!(test_zip_kind("UPPER.GZ", false), none());
143 expect_that!(test_zip_kind("lower.zip.gz", false), none());
144 expect_that!(test_zip_kind("UPPER.ZIP.GZ", false), none());
145 expect_that!(test_zip_kind("lower.jar.gz", false), none());
146 expect_that!(test_zip_kind("UPPER.JAR.GZ", false), none());
147 expect_that!(test_zip_kind("lower.7z.gz", false), none());
148 expect_that!(test_zip_kind("UPPER.7Z.GZ", false), none());
149 expect_that!(test_zip_kind("lower.tar.gz", false), none());
150 expect_that!(test_zip_kind("UPPER.TAR.GZ", false), none());
151 }
152
153 #[gtest]
154 fn test_archive_has_zip_kind() {
155 expect_that!(test_zip_kind("lower", true), none());
156 expect_that!(test_zip_kind("UPPER", true), none());
157 expect_that!(test_zip_kind("lower.zip", true), some(eq(&ZipKind::PkZip)));
158 expect_that!(test_zip_kind("UPPER.ZIP", true), some(eq(&ZipKind::PkZip)));
159 expect_that!(test_zip_kind("lower.jar", true), some(eq(&ZipKind::PkZip)));
160 expect_that!(test_zip_kind("UPPER.JAR", true), some(eq(&ZipKind::PkZip)));
161 expect_that!(test_zip_kind("lower.7z", true), some(eq(&ZipKind::SevenZ)));
162 expect_that!(test_zip_kind("UPPER.7Z", true), some(eq(&ZipKind::SevenZ)));
163 expect_that!(test_zip_kind("lower.tar", true), some(eq(&ZipKind::Tar(false))));
164 expect_that!(test_zip_kind("UPPER.TAR", true), some(eq(&ZipKind::Tar(false))));
165 expect_that!(test_zip_kind("lower.gz", true), none());
166 expect_that!(test_zip_kind("UPPER.GZ", true), none());
167 expect_that!(test_zip_kind("lower.zip.gz", true), none());
168 expect_that!(test_zip_kind("UPPER.ZIP.GZ", true), none());
169 expect_that!(test_zip_kind("lower.jar.gz", true), none());
170 expect_that!(test_zip_kind("UPPER.JAR.GZ", true), none());
171 expect_that!(test_zip_kind("lower.7z.gz", true), none());
172 expect_that!(test_zip_kind("UPPER.7Z.GZ", true), none());
173 expect_that!(test_zip_kind("lower.tar.gz", true), some(eq(&ZipKind::Tar(true))));
174 expect_that!(test_zip_kind("UPPER.TAR.GZ", true), some(eq(&ZipKind::Tar(true))));
175 }
176
177 fn test_zip_kind(path: &str, zip_expand: bool) -> Option<ZipKind> {
178 let path = PathBuf::from(path);
179 ZipKind::from_path(&path, zip_expand)
180 }
181}