@@ -1181,12 +1181,77 @@ fn path_to_uri(path: &Path) -> Result<String> {
11811181
11821182fn url_encode ( segment : & str ) -> String {
11831183 let mut out = String :: new ( ) ;
1184- for ch in segment. chars ( ) {
1185- if ch . is_ascii_alphanumeric ( ) || ch == '-' || ch == '_' || ch == '.' || ch == '~' {
1186- out. push ( ch ) ;
1184+ for byte in segment. bytes ( ) {
1185+ if byte . is_ascii_alphanumeric ( ) || byte == b '-' || byte == b '_' || byte == b '.' || byte == b '~' {
1186+ out. push ( byte as char ) ;
11871187 } else {
1188- out. push_str ( & format ! ( "%{:02X}" , ch as u32 ) ) ;
1188+ out. push_str ( & format ! ( "%{:02X}" , byte ) ) ;
11891189 }
11901190 }
11911191 out
11921192}
1193+
1194+ #[ cfg( test) ]
1195+ mod tests {
1196+ use super :: * ;
1197+
1198+ #[ test]
1199+ fn test_url_encode_ascii ( ) {
1200+ assert_eq ! ( url_encode( "hello" ) , "hello" ) ;
1201+ assert_eq ! ( url_encode( "file.rs" ) , "file.rs" ) ;
1202+ assert_eq ! ( url_encode( "a b" ) , "a%20b" ) ;
1203+ }
1204+
1205+ #[ test]
1206+ fn test_url_encode_multibyte_utf8 ( ) {
1207+ // '€' is U+20AC, UTF-8 bytes: 0xE2 0x82 0xAC
1208+ // Correct percent-encoding: %E2%82%AC
1209+ let encoded = url_encode ( "€" ) ;
1210+ assert_eq ! (
1211+ encoded, "%E2%82%AC" ,
1212+ "Multi-byte UTF-8 chars must be percent-encoded per byte, got: {}" ,
1213+ encoded
1214+ ) ;
1215+ }
1216+
1217+ #[ test]
1218+ fn test_url_encode_two_byte_utf8 ( ) {
1219+ // 'é' is U+00E9, UTF-8 bytes: 0xC3 0xA9
1220+ // Correct percent-encoding: %C3%A9
1221+ let encoded = url_encode ( "café" ) ;
1222+ assert_eq ! (
1223+ encoded, "caf%C3%A9" ,
1224+ "Two-byte UTF-8 chars must be percent-encoded per byte, got: {}" ,
1225+ encoded
1226+ ) ;
1227+ }
1228+
1229+ #[ test]
1230+ fn test_normalize_relative_path ( ) {
1231+ let result = normalize_relative_path ( PathBuf :: from ( "src/../lib/./utils.rs" ) ) ;
1232+ assert_eq ! ( result, PathBuf :: from( "lib/utils.rs" ) ) ;
1233+ }
1234+
1235+ #[ test]
1236+ fn test_normalize_relative_path_no_dots ( ) {
1237+ let result = normalize_relative_path ( PathBuf :: from ( "src/lib/utils.rs" ) ) ;
1238+ assert_eq ! ( result, PathBuf :: from( "src/lib/utils.rs" ) ) ;
1239+ }
1240+
1241+ #[ test]
1242+ fn test_candidate_paths_with_extension ( ) {
1243+ let candidates = candidate_paths ( Path :: new ( "src/lib.rs" ) ) ;
1244+ assert_eq ! ( candidates. len( ) , 1 ) ;
1245+ assert_eq ! ( candidates[ 0 ] , PathBuf :: from( "src/lib.rs" ) ) ;
1246+ }
1247+
1248+ #[ test]
1249+ fn test_candidate_paths_without_extension ( ) {
1250+ let candidates = candidate_paths ( Path :: new ( "src/lib" ) ) ;
1251+ // Should include the original + extensions + directory index files
1252+ assert ! ( candidates. len( ) > 1 ) ;
1253+ assert ! ( candidates. contains( & PathBuf :: from( "src/lib" ) ) ) ;
1254+ assert ! ( candidates. contains( & PathBuf :: from( "src/lib.rs" ) ) ) ;
1255+ assert ! ( candidates. contains( & PathBuf :: from( "src/lib.py" ) ) ) ;
1256+ }
1257+ }
0 commit comments