1use crate::{Error, Result, SPath, TOP_MAX_DEPTH};
2use camino::Utf8PathBuf;
3use globset::{GlobBuilder, GlobSet, GlobSetBuilder};
4
5pub const DEFAULT_EXCLUDE_GLOBS: &[&str] = &["**/.git", "**/.DS_Store"];
6
7pub fn get_glob_set(globs: &[&str]) -> Result<GlobSet> {
8 let mut builder = GlobSetBuilder::new();
9
10 for &glob_str in globs {
11 let glob = GlobBuilder::new(glob_str)
12 .literal_separator(true)
14 .build()
15 .map_err(|e| Error::GlobCantNew {
16 glob: glob_str.to_string(),
17 cause: e,
18 })?;
19 builder.add(glob);
20 }
21
22 let glob_set = builder.build().map_err(|e| Error::GlobSetCantBuild {
23 globs: globs.iter().map(|&v| v.to_string()).collect(),
24 cause: e,
25 })?;
26
27 Ok(glob_set)
28}
29
30pub fn longest_base_path_wild_free(pattern: &SPath) -> SPath {
31 let path = Utf8PathBuf::from(pattern);
32 let mut base_path = Utf8PathBuf::new();
33
34 for component in path.components() {
35 let component_str = component.as_os_str().to_string_lossy();
36 if component_str.contains('*') || component_str.contains('?') {
37 break;
38 }
39 base_path.push(component);
40 }
41
42 SPath::new(base_path)
43}
44
45pub fn get_depth(patterns: &[&str], depth: Option<usize>) -> usize {
55 if let Some(user_depth) = depth {
56 return user_depth;
57 }
58 for &g in patterns {
59 if g.contains("**") {
60 return TOP_MAX_DEPTH;
61 }
62 }
63 let mut max_depth = 0;
64 for &g in patterns {
65 let depth_count = g.matches(['\\', '/']).count() + 1;
66 if depth_count > max_depth {
67 max_depth = depth_count;
68 }
69 }
70 max_depth.max(1)
71}
72
73#[cfg(test)]
76mod tests {
77 use super::*;
78 type Result<T> = core::result::Result<T, Box<dyn std::error::Error>>;
79
80 #[test]
81 fn test_glob_get_depth_no_depth_simple() -> Result<()> {
82 let test_cases: &[(&[&str], usize)] = &[
84 (&["*/*"], 2),
85 (&["some/path/**/and*/"], TOP_MAX_DEPTH),
86 (&["*"], 1),
87 (&["a/b", "c/d/e/f"], 4),
88 (&[], 1),
89 ];
90
91 for &(patterns, expected) in test_cases {
93 let depth = get_depth(patterns, None);
95 assert_eq!(
97 depth, expected,
98 "For patterns {patterns:?}, expected depth {expected}, got {depth}",
99 );
100 }
101 Ok(())
102 }
103
104 #[test]
105 fn test_glob_get_depth_with_depth_custom() -> Result<()> {
106 let test_cases: &[(&[&str], usize, usize)] = &[
108 (&["*/*"], 5, 5),
109 (&["some/path/**/and*/"], 10, 10),
110 (&["*"], 3, 3),
111 (&["a/b", "c/d/e/f"], 7, 7),
112 (&[], 4, 4),
113 ];
114
115 for &(patterns, provided_depth, expected) in test_cases {
117 let depth = get_depth(patterns, Some(provided_depth));
119 assert_eq!(
121 depth, expected,
122 "For patterns {patterns:?} with provided depth {provided_depth}, expected depth {expected}, got {depth}",
123 );
124 }
125 Ok(())
126 }
127}
128
129