@@ -55,6 +55,17 @@ pub struct Url {
55
55
fragment : Option < ~str >
56
56
}
57
57
58
+ #[ deriving( Clone , Eq ) ]
59
+ pub struct Path {
60
+ /// The path component of a URL, for example `/foo/bar`.
61
+ path : ~str ,
62
+ /// The query component of a URL. `~[(~"baz", ~"qux")]` represents the
63
+ /// fragment `baz=qux` in the above example.
64
+ query : Query ,
65
+ /// The fragment component, such as `quz`. Doesn't include the leading `#` character.
66
+ fragment : Option < ~str >
67
+ }
68
+
58
69
/// An optional subcomponent of a URI authority component.
59
70
#[ deriving( Clone , Eq ) ]
60
71
pub struct UserInfo {
@@ -88,6 +99,19 @@ impl Url {
88
99
}
89
100
}
90
101
102
+ impl Path {
103
+ pub fn new ( path : ~str ,
104
+ query : Query ,
105
+ fragment : Option < ~str > )
106
+ -> Path {
107
+ Path {
108
+ path : path,
109
+ query : query,
110
+ fragment : fragment,
111
+ }
112
+ }
113
+ }
114
+
91
115
impl UserInfo {
92
116
#[ inline]
93
117
pub fn new ( user : ~str , pass : Option < ~str > ) -> UserInfo {
@@ -727,6 +751,21 @@ pub fn from_str(rawurl: &str) -> Result<Url, ~str> {
727
751
Ok ( Url :: new ( scheme, userinfo, host, port, path, query, fragment) )
728
752
}
729
753
754
+ pub fn path_from_str ( rawpath : & str ) -> Result < Path , ~str > {
755
+ let ( path, rest) = match get_path ( rawpath, false ) {
756
+ Ok ( val) => val,
757
+ Err ( e) => return Err ( e)
758
+ } ;
759
+
760
+ // query and fragment
761
+ let ( query, fragment) = match get_query_fragment ( rest) {
762
+ Ok ( val) => val,
763
+ Err ( e) => return Err ( e) ,
764
+ } ;
765
+
766
+ Ok ( Path { path : path, query : query, fragment : fragment } )
767
+ }
768
+
730
769
impl FromStr for Url {
731
770
fn from_str ( s : & str ) -> Option < Url > {
732
771
match from_str ( s) {
@@ -736,6 +775,15 @@ impl FromStr for Url {
736
775
}
737
776
}
738
777
778
+ impl FromStr for Path {
779
+ fn from_str ( s : & str ) -> Option < Path > {
780
+ match path_from_str ( s) {
781
+ Ok ( path) => Some ( path) ,
782
+ Err ( _) => None
783
+ }
784
+ }
785
+ }
786
+
739
787
/**
740
788
* Converts a URL from `Url` to string representation.
741
789
*
@@ -780,18 +828,45 @@ pub fn to_str(url: &Url) -> ~str {
780
828
format ! ( "{}:{}{}{}{}" , url. scheme, authority, url. path, query, fragment)
781
829
}
782
830
831
+ pub fn path_to_str( path : & Path ) -> ~str {
832
+ let query = if path. query . is_empty ( ) {
833
+ ~""
834
+ } else {
835
+ format ! ( "?{}" , query_to_str( & path. query) )
836
+ } ;
837
+
838
+ let fragment = match path. fragment {
839
+ Some ( ref fragment) => format ! ( "\\ #{}" , encode_component( * fragment) ) ,
840
+ None => ~"",
841
+ } ;
842
+
843
+ format ! ( "{}{}{}" , path. path, query, fragment)
844
+ }
845
+
783
846
impl ToStr for Url {
784
847
fn to_str ( & self ) -> ~str {
785
848
to_str ( self )
786
849
}
787
850
}
788
851
852
+ impl ToStr for Path {
853
+ fn to_str ( & self ) -> ~str {
854
+ path_to_str ( self )
855
+ }
856
+ }
857
+
789
858
impl IterBytes for Url {
790
859
fn iter_bytes ( & self , lsb0 : bool , f : to_bytes:: Cb ) -> bool {
791
860
self . to_str ( ) . iter_bytes ( lsb0, f)
792
861
}
793
862
}
794
863
864
+ impl IterBytes for Path {
865
+ fn iter_bytes ( & self , lsb0 : bool , f : to_bytes:: Cb ) -> bool {
866
+ self . to_str ( ) . iter_bytes ( lsb0, f)
867
+ }
868
+ }
869
+
795
870
// Put a few tests outside of the 'test' module so they can test the internal
796
871
// functions and those functions don't need 'pub'
797
872
@@ -899,6 +974,17 @@ mod tests {
899
974
assert_eq!(&u.fragment, &Some(~" something"));
900
975
}
901
976
977
+ #[test]
978
+ fn test_path_parse() {
979
+ let path = ~" /doc/~u?s=v#something";
980
+
981
+ let up = path_from_str(path);
982
+ let u = up.unwrap();
983
+ assert_eq!(&u.path, &~" /doc/~u");
984
+ assert_eq!(&u.query, &~[(~" s", ~" v")]);
985
+ assert_eq!(&u.fragment, &Some(~" something"));
986
+ }
987
+
902
988
#[test]
903
989
fn test_url_parse_host_slash() {
904
990
let urlstr = ~" http: //0.42.42.42/";
@@ -907,6 +993,13 @@ mod tests {
907
993
assert!(url.path == ~" /");
908
994
}
909
995
996
+ #[test]
997
+ fn test_path_parse_host_slash() {
998
+ let pathstr = ~" /";
999
+ let path = path_from_str(pathstr).unwrap();
1000
+ assert!(path.path == ~" /");
1001
+ }
1002
+
910
1003
#[test]
911
1004
fn test_url_host_with_port() {
912
1005
let urlstr = ~" scheme: //host:1234";
@@ -930,13 +1023,27 @@ mod tests {
930
1023
assert!( url. path == ~"/file_name. html");
931
1024
}
932
1025
1026
+ #[test]
1027
+ fn test_path_with_underscores() {
1028
+ let pathstr = ~" /file_name. html";
1029
+ let path = path_from_str(pathstr).unwrap();
1030
+ assert!(path.path == ~" /file_name. html");
1031
+ }
1032
+
933
1033
#[test]
934
1034
fn test_url_with_dashes() {
935
1035
let urlstr = ~" http: //dotcom.com/file-name.html";
936
1036
let url = from_str( urlstr) . unwrap( ) ;
937
1037
assert!( url. path == ~"/file-name. html");
938
1038
}
939
1039
1040
+ #[test]
1041
+ fn test_path_with_dashes() {
1042
+ let pathstr = ~" /file-name. html";
1043
+ let path = path_from_str(pathstr).unwrap();
1044
+ assert!(path.path == ~" /file-name. html");
1045
+ }
1046
+
940
1047
#[test]
941
1048
fn test_no_scheme() {
942
1049
assert!(get_scheme(" noschemehere. html").is_err());
@@ -1017,6 +1124,14 @@ mod tests {
1017
1124
assert!(u.query == ~[(~" ba%d ", ~" #& +")]);
1018
1125
}
1019
1126
1127
+ #[test]
1128
+ fn test_path_component_encoding() {
1129
+ let path = ~" /doc%20 uments?ba%25 d%20 =%23 %26 %2 B ";
1130
+ let p = path_from_str(path).unwrap();
1131
+ assert!(p.path == ~" /doc uments");
1132
+ assert!(p.query == ~[(~" ba%d ", ~" #& +")]);
1133
+ }
1134
+
1020
1135
#[test]
1021
1136
fn test_url_without_authority() {
1022
1137
let url = ~" mailto: test@email. com";
0 commit comments