xzip 0.4.0

ZIP CLI with explicit filename encoding for pack and unpack workflows.
Documentation
use globset::{Glob, GlobSet, GlobSetBuilder};

use crate::error::XzipError;

#[derive(Debug)]
pub struct PathFilter {
    include: Option<GlobSet>,
    exclude: Option<GlobSet>,
}

impl PathFilter {
    pub fn new(include: &[String], exclude: &[String]) -> Result<Self, XzipError> {
        Ok(Self {
            include: compile_globs(include)?,
            exclude: compile_globs(exclude)?,
        })
    }

    pub fn allows(&self, path: &str) -> bool {
        let include_ok = self
            .include
            .as_ref()
            .map(|set| set.is_match(path))
            .unwrap_or(true);
        let excluded = self
            .exclude
            .as_ref()
            .map(|set| set.is_match(path))
            .unwrap_or(false);
        include_ok && !excluded
    }
}

fn compile_globs(patterns: &[String]) -> Result<Option<GlobSet>, XzipError> {
    if patterns.is_empty() {
        return Ok(None);
    }

    let mut builder = GlobSetBuilder::new();
    for pattern in patterns {
        let glob =
            Glob::new(pattern).map_err(|_| XzipError::InvalidGlobPattern(pattern.clone()))?;
        builder.add(glob);
    }
    let set = builder
        .build()
        .map_err(|_| XzipError::InvalidGlobPattern("failed to build glob set".to_string()))?;
    Ok(Some(set))
}