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