@@ -55,6 +55,17 @@ pub struct Url {
5555 fragment : Option < ~str >
5656}
5757
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+
5869/// An optional subcomponent of a URI authority component.
5970#[ deriving( Clone , Eq ) ]
6071pub struct UserInfo {
@@ -88,6 +99,19 @@ impl Url {
8899 }
89100}
90101
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+
91115impl UserInfo {
92116 #[ inline]
93117 pub fn new ( user : ~str , pass : Option < ~str > ) -> UserInfo {
@@ -695,6 +719,21 @@ pub fn from_str(rawurl: &str) -> Result<Url, ~str> {
695719 Ok ( Url :: new ( scheme, userinfo, host, port, path, query, fragment) )
696720}
697721
722+ pub fn path_from_str ( rawpath : & str ) -> Result < Path , ~str > {
723+ let ( path, rest) = match get_path ( rawpath, false ) {
724+ Ok ( val) => val,
725+ Err ( e) => return Err ( e)
726+ } ;
727+
728+ // query and fragment
729+ let ( query, fragment) = match get_query_fragment ( rest) {
730+ Ok ( val) => val,
731+ Err ( e) => return Err ( e) ,
732+ } ;
733+
734+ Ok ( Path { path : path, query : query, fragment : fragment } )
735+ }
736+
698737impl FromStr for Url {
699738 fn from_str ( s : & str ) -> Option < Url > {
700739 match from_str ( s) {
@@ -704,6 +743,15 @@ impl FromStr for Url {
704743 }
705744}
706745
746+ impl FromStr for Path {
747+ fn from_str ( s : & str ) -> Option < Path > {
748+ match path_from_str ( s) {
749+ Ok ( path) => Some ( path) ,
750+ Err ( _) => None
751+ }
752+ }
753+ }
754+
707755/**
708756 * Format a `url` as a string
709757 *
@@ -749,18 +797,45 @@ pub fn to_str(url: &Url) -> ~str {
749797 format ! ( "{}:{}{}{}{}" , url. scheme, authority, url. path, query, fragment)
750798}
751799
800+ pub fn path_to_str( path : & Path ) -> ~str {
801+ let query = if path. query . is_empty ( ) {
802+ ~""
803+ } else {
804+ format ! ( "?{}" , query_to_str( & path. query) )
805+ } ;
806+
807+ let fragment = match path. fragment {
808+ Some ( ref fragment) => format ! ( "\\ #{}" , encode_component( * fragment) ) ,
809+ None => ~"",
810+ } ;
811+
812+ format ! ( "{}{}{}" , path. path, query, fragment)
813+ }
814+
752815impl ToStr for Url {
753816 fn to_str ( & self ) -> ~str {
754817 to_str ( self )
755818 }
756819}
757820
821+ impl ToStr for Path {
822+ fn to_str ( & self ) -> ~str {
823+ path_to_str ( self )
824+ }
825+ }
826+
758827impl IterBytes for Url {
759828 fn iter_bytes ( & self , lsb0 : bool , f : to_bytes:: Cb ) -> bool {
760829 self . to_str ( ) . iter_bytes ( lsb0, f)
761830 }
762831}
763832
833+ impl IterBytes for Path {
834+ fn iter_bytes ( & self , lsb0 : bool , f : to_bytes:: Cb ) -> bool {
835+ self . to_str ( ) . iter_bytes ( lsb0, f)
836+ }
837+ }
838+
764839// Put a few tests outside of the 'test' module so they can test the internal
765840// functions and those functions don't need 'pub'
766841
@@ -868,6 +943,17 @@ mod tests {
868943 assert_eq!(&u.fragment, &Some(~" something"));
869944 }
870945
946+ #[test]
947+ fn test_path_parse() {
948+ let path = ~" /doc/~u?s=v#something";
949+
950+ let up = path_from_str(path);
951+ let u = up.unwrap();
952+ assert_eq!(&u.path, &~" /doc/~u");
953+ assert_eq!(&u.query, &~[(~" s", ~" v")]);
954+ assert_eq!(&u.fragment, &Some(~" something"));
955+ }
956+
871957 #[test]
872958 fn test_url_parse_host_slash() {
873959 let urlstr = ~" http: //0.42.42.42/";
@@ -876,6 +962,13 @@ mod tests {
876962 assert!(url.path == ~" /");
877963 }
878964
965+ #[test]
966+ fn test_path_parse_host_slash() {
967+ let pathstr = ~" /";
968+ let path = path_from_str(pathstr).unwrap();
969+ assert!(path.path == ~" /");
970+ }
971+
879972 #[test]
880973 fn test_url_host_with_port() {
881974 let urlstr = ~" scheme: //host:1234";
@@ -899,13 +992,27 @@ mod tests {
899992 assert!( url. path == ~"/file_name. html");
900993 }
901994
995+ #[test]
996+ fn test_path_with_underscores() {
997+ let pathstr = ~" /file_name. html";
998+ let path = path_from_str(pathstr).unwrap();
999+ assert!(path.path == ~" /file_name. html");
1000+ }
1001+
9021002 #[test]
9031003 fn test_url_with_dashes() {
9041004 let urlstr = ~" http: //dotcom.com/file-name.html";
9051005 let url = from_str( urlstr) . unwrap( ) ;
9061006 assert!( url. path == ~"/file-name. html");
9071007 }
9081008
1009+ #[test]
1010+ fn test_path_with_dashes() {
1011+ let pathstr = ~" /file-name. html";
1012+ let path = path_from_str(pathstr).unwrap();
1013+ assert!(path.path == ~" /file-name. html");
1014+ }
1015+
9091016 #[test]
9101017 fn test_no_scheme() {
9111018 assert!(get_scheme(" noschemehere. html").is_err());
@@ -986,6 +1093,14 @@ mod tests {
9861093 assert!(u.query == ~[(~" ba%d ", ~" #& +")]);
9871094 }
9881095
1096+ #[test]
1097+ fn test_path_component_encoding() {
1098+ let path = ~" /doc%20 uments?ba%25 d%20 =%23 %26 %2 B ";
1099+ let p = path_from_str(path).unwrap();
1100+ assert!(p.path == ~" /doc uments");
1101+ assert!(p.query == ~[(~" ba%d ", ~" #& +")]);
1102+ }
1103+
9891104 #[test]
9901105 fn test_url_without_authority() {
9911106 let url = ~" mailto: test@email. com";
0 commit comments