1pub fn encode_path(path: &str) -> String {
32 if path.is_empty() || path == "/" {
34 return path.to_string();
35 }
36
37 let segments: Vec<String> = path
39 .split('/')
40 .map(|segment| {
41 if segment.is_empty() {
42 segment.to_string()
44 } else {
45 percent_encode(segment)
48 }
49 })
50 .collect();
51
52 segments.join("/")
53}
54
55fn percent_encode(s: &str) -> String {
62 let mut encoded = String::new();
63
64 for byte in s.bytes() {
65 match byte {
66 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
68 encoded.push(byte as char);
69 }
70 _ => {
72 encoded.push_str(&format!("%{:02X}", byte));
73 }
74 }
75 }
76
77 encoded
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_encode_simple_path() {
86 assert_eq!(encode_path("/simple/path.txt"), "/simple/path.txt");
87 }
88
89 #[test]
90 fn test_encode_path_with_spaces() {
91 assert_eq!(
92 encode_path("/my folder/my file.txt"),
93 "/my%20folder/my%20file.txt"
94 );
95 }
96
97 #[test]
98 fn test_encode_path_with_brackets() {
99 assert_eq!(
100 encode_path("/data/file[2024].txt"),
101 "/data/file%5B2024%5D.txt"
102 );
103 }
104
105 #[test]
106 fn test_encode_path_with_unicode() {
107 assert_eq!(
109 encode_path("/文档/测试.txt"),
110 "/%E6%96%87%E6%A1%A3/%E6%B5%8B%E8%AF%95.txt"
111 );
112
113 assert_eq!(
115 encode_path("/папка/файл.txt"),
116 "/%D0%BF%D0%B0%D0%BF%D0%BA%D0%B0/%D1%84%D0%B0%D0%B9%D0%BB.txt"
117 );
118 }
119
120 #[test]
121 fn test_encode_path_with_quotes() {
122 assert_eq!(
123 encode_path("/\"quoted\"/file.txt"),
124 "/%22quoted%22/file.txt"
125 );
126 }
127
128 #[test]
129 fn test_encode_path_with_special_chars() {
130 assert_eq!(encode_path("/data/file@#$.txt"), "/data/file%40%23%24.txt");
131 }
132
133 #[test]
134 fn test_encode_empty_path() {
135 assert_eq!(encode_path(""), "");
136 }
137
138 #[test]
139 fn test_encode_root_path() {
140 assert_eq!(encode_path("/"), "/");
141 }
142
143 #[test]
144 fn test_encode_path_preserves_leading_slash() {
145 assert_eq!(encode_path("/folder/file"), "/folder/file");
146 }
147
148 #[test]
149 fn test_encode_path_without_leading_slash() {
150 assert_eq!(encode_path("folder/file"), "folder/file");
151 }
152
153 #[test]
154 fn test_encode_path_with_trailing_slash() {
155 assert_eq!(encode_path("/folder/"), "/folder/");
156 }
157
158 #[test]
159 fn test_encode_complex_path() {
160 assert_eq!(
162 encode_path("/my folder/data [2024]/文档.txt"),
163 "/my%20folder/data%20%5B2024%5D/%E6%96%87%E6%A1%A3.txt"
164 );
165 }
166}