use crate::{Error, Result, SPath, TOP_MAX_DEPTH};
use camino::Utf8PathBuf;
use globset::{GlobBuilder, GlobSet, GlobSetBuilder};
pub const DEFAULT_EXCLUDE_GLOBS: &[&str] = &["**/.git", "**/.DS_Store"];
pub fn get_glob_set(globs: &[&str]) -> Result<GlobSet> {
let mut builder = GlobSetBuilder::new();
for &glob_str in globs {
let glob = GlobBuilder::new(glob_str)
.literal_separator(true)
.build()
.map_err(|e| Error::GlobCantNew {
glob: glob_str.to_string(),
cause: e,
})?;
builder.add(glob);
}
let glob_set = builder.build().map_err(|e| Error::GlobSetCantBuild {
globs: globs.iter().map(|&v| v.to_string()).collect(),
cause: e,
})?;
Ok(glob_set)
}
pub fn longest_base_path_wild_free(pattern: &SPath) -> SPath {
let path = Utf8PathBuf::from(pattern);
let mut base_path = Utf8PathBuf::new();
for component in path.components() {
let component_str = component.as_os_str().to_string_lossy();
if component_str.contains('*') || component_str.contains('?') {
break;
}
base_path.push(component);
}
SPath::new(base_path)
}
pub fn get_depth(patterns: &[&str], depth: Option<usize>) -> usize {
if let Some(user_depth) = depth {
return user_depth;
}
for &g in patterns {
if g.contains("**") {
return TOP_MAX_DEPTH;
}
}
let mut max_depth = 0;
for &g in patterns {
let depth_count = g.matches(['\\', '/']).count() + 1;
if depth_count > max_depth {
max_depth = depth_count;
}
}
max_depth.max(1)
}
#[cfg(test)]
mod tests {
use super::*;
type Result<T> = core::result::Result<T, Box<dyn std::error::Error>>;
#[test]
fn test_glob_get_depth_no_depth_simple() -> Result<()> {
let test_cases: &[(&[&str], usize)] = &[
(&["*/*"], 2),
(&["some/path/**/and*/"], TOP_MAX_DEPTH),
(&["*"], 1),
(&["a/b", "c/d/e/f"], 4),
(&[], 1),
];
for &(patterns, expected) in test_cases {
let depth = get_depth(patterns, None);
assert_eq!(
depth, expected,
"For patterns {patterns:?}, expected depth {expected}, got {depth}",
);
}
Ok(())
}
#[test]
fn test_glob_get_depth_with_depth_custom() -> Result<()> {
let test_cases: &[(&[&str], usize, usize)] = &[
(&["*/*"], 5, 5),
(&["some/path/**/and*/"], 10, 10),
(&["*"], 3, 3),
(&["a/b", "c/d/e/f"], 7, 7),
(&[], 4, 4),
];
for &(patterns, provided_depth, expected) in test_cases {
let depth = get_depth(patterns, Some(provided_depth));
assert_eq!(
depth, expected,
"For patterns {patterns:?} with provided depth {provided_depth}, expected depth {expected}, got {depth}",
);
}
Ok(())
}
}