dodot_lib/preprocessing/
no_reverse.rs1use std::path::Path;
15
16pub fn is_no_reverse(source_path: &Path, patterns: &[String]) -> bool {
22 if patterns.is_empty() {
23 return false;
24 }
25 let Some(filename) = source_path.file_name().and_then(|f| f.to_str()) else {
26 return false;
27 };
28 patterns.iter().any(|p| {
29 glob::Pattern::new(p)
30 .map(|g| g.matches(filename))
31 .unwrap_or(false)
32 })
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38 use std::path::PathBuf;
39
40 #[test]
41 fn empty_patterns_never_match() {
42 let p = PathBuf::from("/dotfiles/app/config.toml.tmpl");
43 assert!(!is_no_reverse(&p, &[]));
44 }
45
46 #[test]
47 fn exact_filename_match() {
48 let p = PathBuf::from("/dotfiles/app/complex-config.toml.tmpl");
49 assert!(is_no_reverse(&p, &["complex-config.toml.tmpl".to_string()]));
50 assert!(!is_no_reverse(&p, &["other-config.toml.tmpl".to_string()]));
51 }
52
53 #[test]
54 fn glob_matches_by_suffix() {
55 let p = PathBuf::from("/dotfiles/app/foo.gen.tmpl");
56 assert!(is_no_reverse(&p, &["*.gen.tmpl".to_string()]));
57 }
58
59 #[test]
60 fn glob_matches_one_of_many() {
61 let p = PathBuf::from("/dotfiles/app/foo.gen.tmpl");
62 let patterns = vec![
63 "first.tmpl".to_string(),
64 "*.gen.tmpl".to_string(),
65 "third.tmpl".to_string(),
66 ];
67 assert!(is_no_reverse(&p, &patterns));
68 }
69
70 #[test]
71 fn no_match_returns_false() {
72 let p = PathBuf::from("/dotfiles/app/regular.tmpl");
73 assert!(!is_no_reverse(&p, &["*.gen.tmpl".to_string()]));
74 }
75
76 #[test]
77 fn invalid_glob_pattern_is_ignored() {
78 let p = PathBuf::from("/dotfiles/app/cfg.tmpl");
82 assert!(!is_no_reverse(&p, &["[unclosed".to_string()]));
83 }
84
85 #[test]
86 fn matches_against_filename_not_full_path() {
87 let p = PathBuf::from("/dotfiles/strange-name/cfg.tmpl");
90 assert!(is_no_reverse(&p, &["cfg.tmpl".to_string()]));
91 assert!(!is_no_reverse(&p, &["strange-name".to_string()]));
92 }
93}