ex_cli/zip/
wrapper.rs

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