ex-cli 1.21.0

Command line tool to find, filter, sort and list files.
Documentation
use crate::cli::hidden::HiddenKind;
use crate::config::Config;
use crate::error::MyResult;
use crate::fs::entry::{Entry, EntryResult};
use crate::fs::system::FileSystem;
use crate::zip::manager::PasswordManager;
use crate::zip::parent::ZipParent;
use crate::zip::{pkzip, seven, tar};
use std::path::{Component, Path, PathBuf};
use walkdir::DirEntry;

#[derive(Debug, PartialEq)]
pub enum ZipKind {
    PkZip,
    SevenZ,
    Tar(bool),
}

impl ZipKind {
    pub fn from_path(path: &Path, zip_expand: bool) -> Option<Self> {
        if zip_expand {
            if let Some(ext) = path.extension() {
                let ext = ext.to_ascii_lowercase();
                if ext == "zip" || ext == "jar" {
                    return Some(Self::PkZip);
                }
                if ext == "7z" {
                    return Some(Self::SevenZ);
                }
                if ext == "tar" {
                    return Some(Self::Tar(false));
                }
                if ext == "gz" {
                    let path = path.with_extension("");
                    if let Some(ext) = path.extension() {
                        let ext = ext.to_ascii_lowercase();
                        if ext == "tar" {
                            return Some(Self::Tar(true));
                        }
                    }
                }
            }
        }
        None
    }

    pub fn walk_entries<F: Fn(EntryResult)>(
        &self,
        config: &Config,
        dir_entry: &DirEntry,
        zip_manager: &mut PasswordManager,
        function: &F,
    ) -> MyResult<()> {
        let parent_path = PathBuf::from(dir_entry.path());
        let parent_depth = dir_entry.depth();
        match ZipParent::new(parent_path, parent_depth) {
            Ok(zip_parent) => {
                self.walk_inner(config, &zip_parent, zip_manager, &|entry| {
                    match entry {
                        Ok(entry) if filter_entry(config, entry) => function(Ok(entry)),
                        Ok(_) => (),
                        Err(error) => function(Err(error)),
                    }
                })
            }
            Err(error) => {
                function(Err(error));
                Ok(())
            }
        }
    }

    fn walk_inner<F: Fn(EntryResult)>(
        &self,
        config: &Config,
        zip_parent: &ZipParent,
        zip_manager: &mut PasswordManager,
        function: &F,
    ) -> MyResult<()> {
        match self {
            Self::PkZip => pkzip::walk_entries(zip_parent, zip_manager, config.want_decrypt(), function),
            Self::SevenZ => seven::walk_entries(zip_parent, zip_manager, config.want_decrypt(), function),
            Self::Tar(want_gzip) => tar::walk_entries(zip_parent, *want_gzip, function),
        }
    }
}

fn filter_entry(config: &Config, entry: &dyn Entry) -> bool {
    if let Some(max_depth) = config.max_depth() {
        if entry.file_depth() > max_depth {
            return false;
        }
    }
    if let Some(inner_path) = entry.inner_path() {
        match config.show_hidden() {
            HiddenKind::None => {
                return filter_hidden_path(inner_path);
            }
            HiddenKind::Files => {
                if let Some(parent) = inner_path.parent() {
                    return filter_hidden_path(parent);
                }
            }
            HiddenKind::Recurse => {
                return true;
            }
        }
    }
    true
}

fn filter_hidden_path(path: &Path) -> bool {
    path.components().all(filter_hidden_name)
}

fn filter_hidden_name(component: Component) -> bool {
    match component {
        Component::Normal(name) => !FileSystem::is_hidden_name(name.to_str()),
        _ => true,
    }
}

#[cfg(test)]
mod tests {
    use crate::zip::wrapper::ZipKind;
    use std::path::PathBuf;

    #[test]
    fn test_file_has_zip_kind() {
        assert_eq!(None, test_zip_kind("lower", false));
        assert_eq!(None, test_zip_kind("UPPER", false));
        assert_eq!(None, test_zip_kind("lower.zip", false));
        assert_eq!(None, test_zip_kind("UPPER.ZIP", false));
        assert_eq!(None, test_zip_kind("lower.jar", false));
        assert_eq!(None, test_zip_kind("UPPER.JAR", false));
        assert_eq!(None, test_zip_kind("lower.7z", false));
        assert_eq!(None, test_zip_kind("UPPER.7Z", false));
        assert_eq!(None, test_zip_kind("lower.tar", false));
        assert_eq!(None, test_zip_kind("UPPER.TAR", false));
        assert_eq!(None, test_zip_kind("lower.gz", false));
        assert_eq!(None, test_zip_kind("UPPER.GZ", false));
        assert_eq!(None, test_zip_kind("lower.zip.gz", false));
        assert_eq!(None, test_zip_kind("UPPER.ZIP.GZ", false));
        assert_eq!(None, test_zip_kind("lower.jar.gz", false));
        assert_eq!(None, test_zip_kind("UPPER.JAR.GZ", false));
        assert_eq!(None, test_zip_kind("lower.7z.gz", false));
        assert_eq!(None, test_zip_kind("UPPER.7Z.GZ", false));
        assert_eq!(None, test_zip_kind("lower.tar.gz", false));
        assert_eq!(None, test_zip_kind("UPPER.TAR.GZ", false));
    }

    #[test]
    fn test_archive_has_zip_kind() {
        assert_eq!(None, test_zip_kind("lower", true));
        assert_eq!(None, test_zip_kind("UPPER", true));
        assert_eq!(Some(ZipKind::PkZip), test_zip_kind("lower.zip", true));
        assert_eq!(Some(ZipKind::PkZip), test_zip_kind("UPPER.ZIP", true));
        assert_eq!(Some(ZipKind::PkZip), test_zip_kind("lower.jar", true));
        assert_eq!(Some(ZipKind::PkZip), test_zip_kind("UPPER.JAR", true));
        assert_eq!(Some(ZipKind::SevenZ), test_zip_kind("lower.7z", true));
        assert_eq!(Some(ZipKind::SevenZ), test_zip_kind("UPPER.7Z", true));
        assert_eq!(Some(ZipKind::Tar(false)), test_zip_kind("lower.tar", true));
        assert_eq!(Some(ZipKind::Tar(false)), test_zip_kind("UPPER.TAR", true));
        assert_eq!(None, test_zip_kind("lower.gz", true));
        assert_eq!(None, test_zip_kind("UPPER.GZ", true));
        assert_eq!(None, test_zip_kind("lower.zip.gz", true));
        assert_eq!(None, test_zip_kind("UPPER.ZIP.GZ", true));
        assert_eq!(None, test_zip_kind("lower.jar.gz", true));
        assert_eq!(None, test_zip_kind("UPPER.JAR.GZ", true));
        assert_eq!(None, test_zip_kind("lower.7z.gz", true));
        assert_eq!(None, test_zip_kind("UPPER.7Z.GZ", true));
        assert_eq!(Some(ZipKind::Tar(true)), test_zip_kind("lower.tar.gz", true));
        assert_eq!(Some(ZipKind::Tar(true)), test_zip_kind("UPPER.TAR.GZ", true));
    }

    fn test_zip_kind(path: &str, zip_expand: bool) -> Option<ZipKind> {
        let path = PathBuf::from(path);
        ZipKind::from_path(&path, zip_expand)
    }
}