Skip to main content

ex_cli/zip/
wrapper.rs

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}