yew_nav_link/utils/
path.rs1#[must_use]
32pub fn normalize_path(path: &str) -> String {
33 let mut result = String::with_capacity(path.len());
34 let mut prev_was_slash = false;
35
36 for ch in path.chars() {
37 if ch == '/' {
38 if !prev_was_slash {
39 result.push(ch);
40 prev_was_slash = true;
41 }
42 } else {
43 result.push(ch);
44 prev_was_slash = false;
45 }
46 }
47
48 if result.len() > 1 && result.ends_with('/') {
49 result.pop();
50 }
51
52 result
53}
54
55#[must_use]
57pub fn is_absolute(path: &str) -> bool {
58 path.starts_with('/')
59}
60
61#[must_use]
66pub fn join_paths(base: &str, path: &str) -> String {
67 if path.starts_with('/') {
68 normalize_path(path)
69 } else {
70 let base = base.trim_end_matches('/');
71 normalize_path(&format!("{base}/{path}"))
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn normalize_removes_double_slashes() {
81 assert_eq!(normalize_path("/docs//api"), "/docs/api");
82 assert_eq!(normalize_path("a//b///c"), "a/b/c");
83 }
84
85 #[test]
86 fn normalize_removes_trailing_slash() {
87 assert_eq!(normalize_path("/docs/"), "/docs");
88 assert_eq!(normalize_path("/"), "/");
89 }
90
91 #[test]
92 fn is_absolute_returns_true_for_rooted_paths() {
93 assert!(is_absolute("/"));
94 assert!(is_absolute("/docs"));
95 assert!(is_absolute("/docs/api"));
96 }
97
98 #[test]
99 fn is_absolute_returns_false_for_relative_paths() {
100 assert!(!is_absolute(""));
101 assert!(!is_absolute("docs"));
102 assert!(!is_absolute("./docs"));
103 assert!(!is_absolute("../docs"));
104 }
105
106 #[test]
107 fn join_paths_with_absolute() {
108 assert_eq!(join_paths("/base", "/docs"), "/docs");
109 assert_eq!(join_paths("/base", "/"), "/");
110 }
111
112 #[test]
113 fn join_paths_with_relative() {
114 assert_eq!(join_paths("/base", "docs"), "/base/docs");
115 assert_eq!(join_paths("/base/", "docs"), "/base/docs");
116 }
117}