Skip to main content

tokmd_path/
lib.rs

1//! Single-responsibility path normalization for deterministic matching.
2
3/// Normalize path separators to `/`.
4#[must_use]
5pub fn normalize_slashes(path: &str) -> String {
6    if path.contains('\\') {
7        path.replace('\\', "/")
8    } else {
9        path.to_string()
10    }
11}
12
13/// Normalize a relative path for matching:
14/// - converts `\` to `/`
15/// - strips one leading `./`
16#[must_use]
17pub fn normalize_rel_path(path: &str) -> String {
18    let normalized = normalize_slashes(path);
19    if let Some(stripped) = normalized.strip_prefix("./") {
20        stripped.to_string()
21    } else {
22        normalized
23    }
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29    use proptest::prelude::*;
30
31    #[test]
32    fn normalize_slashes_replaces_backslash() {
33        assert_eq!(normalize_slashes(r"foo\bar\baz.rs"), "foo/bar/baz.rs");
34    }
35
36    #[test]
37    fn normalize_rel_path_strips_dot_slash() {
38        assert_eq!(normalize_rel_path("./src/main.rs"), "src/main.rs");
39    }
40
41    #[test]
42    fn normalize_rel_path_strips_dot_backslash() {
43        assert_eq!(normalize_rel_path(r".\src\main.rs"), "src/main.rs");
44    }
45
46    #[test]
47    fn normalize_rel_path_preserves_non_relative_prefix() {
48        assert_eq!(normalize_rel_path("../src/main.rs"), "../src/main.rs");
49    }
50
51    proptest! {
52        #[test]
53        fn normalize_slashes_no_backslashes(path in "\\PC*") {
54            let normalized = normalize_slashes(&path);
55            prop_assert!(!normalized.contains('\\'));
56        }
57
58        #[test]
59        fn normalize_slashes_idempotent(path in "\\PC*") {
60            let once = normalize_slashes(&path);
61            let twice = normalize_slashes(&once);
62            prop_assert_eq!(once, twice);
63        }
64
65        #[test]
66        fn normalize_rel_path_no_backslashes(path in "\\PC*") {
67            let normalized = normalize_rel_path(&path);
68            prop_assert!(!normalized.contains('\\'));
69        }
70
71        #[test]
72        fn normalize_rel_path_idempotent(path in "\\PC*") {
73            let once = normalize_rel_path(&path);
74            let twice = normalize_rel_path(&once);
75            prop_assert_eq!(once, twice);
76        }
77    }
78}