perl_workspace_ignore/
lib.rs1#![deny(unsafe_code)]
7#![warn(rust_2018_idioms)]
8#![warn(missing_docs)]
9#![warn(clippy::all)]
10
11use std::path::{Component, Path};
12
13const SKIPPED_DIRS: [&str; 6] = [".git", ".hg", ".svn", "target", "node_modules", ".cache"];
14
15#[must_use]
17pub fn is_skipped_dir_name(name: &str) -> bool {
18 SKIPPED_DIRS.contains(&name)
19}
20
21#[must_use]
23pub fn path_contains_skipped_component(path: &Path) -> bool {
24 for component in path.components() {
25 if let Component::Normal(name) = component
26 && let Some(value) = name.to_str()
27 && is_skipped_dir_name(value)
28 {
29 return true;
30 }
31 }
32
33 false
34}
35
36#[cfg(test)]
37mod tests {
38 use super::{SKIPPED_DIRS, is_skipped_dir_name, path_contains_skipped_component};
39 use std::path::Path;
40
41 #[test]
44 fn test_skipped_dir_name_git_matches() {
45 assert!(is_skipped_dir_name(".git"));
46 }
47
48 #[test]
49 fn test_skipped_dir_name_hg_matches() {
50 assert!(is_skipped_dir_name(".hg"));
51 }
52
53 #[test]
54 fn test_skipped_dir_name_svn_matches() {
55 assert!(is_skipped_dir_name(".svn"));
56 }
57
58 #[test]
59 fn test_skipped_dir_name_target_matches() {
60 assert!(is_skipped_dir_name("target"));
61 }
62
63 #[test]
64 fn test_skipped_dir_name_node_modules_matches() {
65 assert!(is_skipped_dir_name("node_modules"));
66 }
67
68 #[test]
69 fn test_skipped_dir_name_cache_matches() {
70 assert!(is_skipped_dir_name(".cache"));
71 }
72
73 #[test]
74 fn test_skipped_dir_name_all_canonical_patterns() {
75 for name in &SKIPPED_DIRS {
77 assert!(is_skipped_dir_name(name), "expected '{name}' to be skipped");
78 }
79 }
80
81 #[test]
82 fn test_skipped_dir_name_constant_has_expected_count() {
83 assert_eq!(SKIPPED_DIRS.len(), 6);
84 }
85
86 #[test]
89 fn test_skipped_dir_name_rejects_common_directories() {
90 for name in ["src", "lib", "blib", "tmp", "vendor", "dist", "build"] {
91 assert!(!is_skipped_dir_name(name), "'{name}' should not be skipped");
92 }
93 }
94
95 #[test]
96 fn test_skipped_dir_name_case_sensitive_git() {
97 assert!(!is_skipped_dir_name(".Git"));
99 assert!(!is_skipped_dir_name(".GIT"));
100 assert!(!is_skipped_dir_name(".GIT"));
101 }
102
103 #[test]
104 fn test_skipped_dir_name_case_sensitive_target() {
105 assert!(!is_skipped_dir_name("Target"));
106 assert!(!is_skipped_dir_name("TARGET"));
107 }
108
109 #[test]
110 fn test_skipped_dir_name_case_sensitive_node_modules() {
111 assert!(!is_skipped_dir_name("Node_Modules"));
112 assert!(!is_skipped_dir_name("NODE_MODULES"));
113 }
114
115 #[test]
116 fn test_skipped_dir_name_prefix_suffix_not_matched() {
117 assert!(!is_skipped_dir_name(".git2"));
119 assert!(!is_skipped_dir_name("git"));
120 assert!(!is_skipped_dir_name(".gitignore"));
121 assert!(!is_skipped_dir_name("my_target"));
122 assert!(!is_skipped_dir_name("targets"));
123 assert!(!is_skipped_dir_name("node_modules_backup"));
124 }
125
126 #[test]
127 fn test_skipped_dir_name_empty_string() {
128 assert!(!is_skipped_dir_name(""));
129 }
130
131 #[test]
132 fn test_skipped_dir_name_whitespace() {
133 assert!(!is_skipped_dir_name(" "));
134 assert!(!is_skipped_dir_name(" .git"));
135 assert!(!is_skipped_dir_name(".git "));
136 }
137
138 #[test]
139 fn test_skipped_dir_name_dot_only() {
140 assert!(!is_skipped_dir_name("."));
141 assert!(!is_skipped_dir_name(".."));
142 }
143
144 #[test]
145 fn test_skipped_dir_name_slash_embedded() {
146 assert!(!is_skipped_dir_name(".git/objects"));
148 assert!(!is_skipped_dir_name("node_modules/pkg"));
149 }
150
151 #[test]
154 fn test_path_skipped_component_git_at_root() {
155 assert!(path_contains_skipped_component(Path::new(".git")));
156 }
157
158 #[test]
159 fn test_path_skipped_component_git_nested() {
160 assert!(path_contains_skipped_component(Path::new("repo/.git/objects/pack")));
161 }
162
163 #[test]
164 fn test_path_skipped_component_node_modules_middle() {
165 assert!(path_contains_skipped_component(Path::new(
166 "workspace/node_modules/Some/Module.pm"
167 )));
168 }
169
170 #[test]
171 fn test_path_skipped_component_target_leaf() {
172 assert!(path_contains_skipped_component(Path::new("project/target")));
173 }
174
175 #[test]
176 fn test_path_skipped_component_each_pattern_in_path() {
177 for name in &SKIPPED_DIRS {
178 let p = format!("some/dir/{name}/file.txt");
179 assert!(
180 path_contains_skipped_component(Path::new(&p)),
181 "path containing '{name}' should be flagged"
182 );
183 }
184 }
185
186 #[test]
189 fn test_path_clean_lib_path_not_skipped() {
190 assert!(!path_contains_skipped_component(Path::new("repo/lib/My/Module.pm")));
191 }
192
193 #[test]
194 fn test_path_clean_src_path_not_skipped() {
195 assert!(!path_contains_skipped_component(Path::new("crates/perl-parser/src/lib.rs")));
196 }
197
198 #[test]
199 fn test_path_clean_deep_nesting_not_skipped() {
200 assert!(!path_contains_skipped_component(Path::new("a/b/c/d/e/f/g/h/file.pm")));
201 }
202
203 #[test]
206 fn test_path_multiple_skipped_components() {
207 assert!(path_contains_skipped_component(Path::new(".git/node_modules/something")));
209 }
210
211 #[test]
212 fn test_path_skipped_inside_skipped() {
213 assert!(path_contains_skipped_component(Path::new("target/.cache/build")));
214 }
215
216 #[test]
217 fn test_path_deeply_nested_skipped() {
218 assert!(path_contains_skipped_component(Path::new("a/b/c/d/e/.svn/f/g")));
219 }
220
221 #[test]
224 fn test_path_empty_path_not_skipped() {
225 assert!(!path_contains_skipped_component(Path::new("")));
226 }
227
228 #[test]
229 fn test_path_dot_not_skipped() {
230 assert!(!path_contains_skipped_component(Path::new(".")));
231 }
232
233 #[test]
234 fn test_path_dotdot_not_skipped() {
235 assert!(!path_contains_skipped_component(Path::new("..")));
236 }
237
238 #[test]
239 fn test_path_root_only_not_skipped() {
240 assert!(!path_contains_skipped_component(Path::new("/")));
241 }
242
243 #[test]
244 fn test_path_absolute_with_skipped() {
245 assert!(path_contains_skipped_component(Path::new("/home/user/project/.git/config")));
246 }
247
248 #[test]
249 fn test_path_absolute_without_skipped() {
250 assert!(!path_contains_skipped_component(Path::new("/home/user/project/lib/Module.pm")));
251 }
252
253 #[test]
254 fn test_path_trailing_slash_with_skipped() {
255 assert!(path_contains_skipped_component(Path::new("repo/.git/")));
257 }
258
259 #[test]
260 fn test_path_dotdot_before_skipped() {
261 assert!(path_contains_skipped_component(Path::new("../other/node_modules/pkg")));
262 }
263
264 #[test]
265 fn test_path_dot_before_skipped() {
266 assert!(path_contains_skipped_component(Path::new("./target/debug/binary")));
267 }
268
269 #[test]
270 fn test_path_skipped_name_as_file_extension_not_matched() {
271 assert!(!path_contains_skipped_component(Path::new("dir/file.target")));
273 }
274
275 #[test]
276 fn test_path_skipped_name_substring_in_component() {
277 assert!(!path_contains_skipped_component(Path::new("project/my_target_dir/file.rs")));
279 }
280
281 #[test]
282 fn test_path_only_skipped_component() {
283 assert!(path_contains_skipped_component(Path::new("node_modules")));
285 assert!(path_contains_skipped_component(Path::new(".hg")));
286 }
287
288 #[test]
289 fn test_path_single_non_skipped_component() {
290 assert!(!path_contains_skipped_component(Path::new("src")));
291 assert!(!path_contains_skipped_component(Path::new("lib")));
292 }
293
294 #[test]
295 fn test_path_unicode_component_not_skipped() {
296 assert!(!path_contains_skipped_component(Path::new("repo/\u{00e9}t\u{00e9}/Module.pm")));
297 }
298
299 #[test]
300 fn test_path_case_sensitive_git_in_path() {
301 assert!(!path_contains_skipped_component(Path::new("repo/.Git/config")));
302 assert!(!path_contains_skipped_component(Path::new("repo/.GIT/config")));
303 }
304}