1#![forbid(unsafe_code)]
4
5use std::path::Path;
6
7use tokmd_path::normalize_rel_path;
8
9#[must_use]
27pub fn normalize_exclude_pattern(root: &Path, path: &Path) -> String {
28 let rel = if path.is_absolute() {
29 path.strip_prefix(root).unwrap_or(path)
30 } else {
31 path
32 };
33 normalize_rel_path(&rel.to_string_lossy())
34}
35
36#[must_use]
48pub fn has_exclude_pattern(existing: &[String], pattern: &str) -> bool {
49 let normalized = normalize_rel_path(pattern);
50 existing
51 .iter()
52 .any(|candidate| normalize_rel_path(candidate) == normalized)
53}
54
55pub fn add_exclude_pattern(existing: &mut Vec<String>, pattern: String) -> bool {
71 if pattern.is_empty() || has_exclude_pattern(existing, &pattern) {
72 return false;
73 }
74 existing.push(pattern);
75 true
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn normalize_exclude_pattern_strips_root_for_absolute_paths() {
84 let root = std::env::temp_dir().join("tokmd-exclude-lib-root");
85 let path = root.join(".handoff").join("manifest.json");
86 let got = normalize_exclude_pattern(&root, &path);
87 assert_eq!(got, ".handoff/manifest.json");
88 }
89
90 #[test]
91 fn normalize_exclude_pattern_keeps_outside_absolute_paths() {
92 let root = std::env::temp_dir().join("tokmd-exclude-lib-root");
93 let outside = std::env::temp_dir()
94 .join("tokmd-exclude-lib-outside")
95 .join("bundle.txt");
96 let got = normalize_exclude_pattern(&root, &outside);
97 let expected = tokmd_path::normalize_rel_path(&outside.to_string_lossy());
98 assert_eq!(got, expected);
99 }
100
101 #[test]
102 fn add_exclude_pattern_dedupes_after_normalization() {
103 let mut existing = vec!["./out\\bundle".to_string()];
104 assert!(!add_exclude_pattern(
105 &mut existing,
106 "out/bundle".to_string()
107 ));
108 assert_eq!(existing.len(), 1);
109 }
110
111 #[test]
112 fn add_exclude_pattern_rejects_empty_patterns() {
113 let mut existing = vec![];
114 assert!(!add_exclude_pattern(&mut existing, String::new()));
115 assert!(existing.is_empty());
116 }
117}