Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libstd/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ export tcp;

import ip = net_ip;
export ip;

import url = net_url;
export url;
241 changes: 241 additions & 0 deletions src/libstd/net_url.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
//! Types/fns concerning URLs (see RFC 3986)

import map;
import map::*;

export url, userinfo, query, from_str, to_str;

type url = {
scheme: str,
user: option<userinfo>,
host: str,
path: str,
query: query,
fragment: option<str>
};

type userinfo = {
user: str,
pass: option<str>
};

type query = map::hashmap<str, @str>;

fn url(-scheme: str, -user: option<userinfo>, -host: str,
-path: str, -query: query, -fragment: option<str>) -> url {
{ scheme: scheme, user: user, host: host,
path: path, query: query, fragment: fragment }
}

fn userinfo(-user: str, -pass: option<str>) -> userinfo {
{user: user, pass: pass}
}

fn split_char_first(s: str, c: char) -> (str, str) {
let mut v = str::splitn_char(s, c, 1);
if v.len() == 1 {
ret (s, "");
} else {
ret (vec::shift(v), vec::pop(v));
}
}

fn userinfo_from_str(uinfo: str) -> userinfo {
let (user, p) = split_char_first(uinfo, ':');
let pass = if str::len(p) == 0 {
option::none
} else {
option::some(p)
};
ret userinfo(user, pass);
}

fn userinfo_to_str(-userinfo: userinfo) -> str {
if option::is_some(userinfo.pass) {
ret str::concat(~[copy userinfo.user, ":",
option::unwrap(copy userinfo.pass),
"@"]);
} else {
ret str::concat(~[copy userinfo.user, "@"]);
}
}

fn query_from_str(rawquery: str) -> query {
let query: query = map::str_hash();
if str::len(rawquery) != 0 {
for str::split_char(rawquery, '&').each |p| {
let (k, v) = split_char_first(p, '=');
query.insert(k, @v);
};
}
ret query;
}

fn query_to_str(query: query) -> str {
let mut strvec = ~[];
for query.each |k, v| {
strvec += ~[#fmt("%s=%s", k, *v)];
};
ret str::connect(strvec, "&");
}

fn get_scheme(rawurl: str) -> option::option<(str, str)> {
for str::each_chari(rawurl) |i,c| {
if char::is_alphabetic(c) {
cont;
} else if c == ':' && i != 0 {
ret option::some((rawurl.slice(0,i),
rawurl.slice(i+3,str::len(rawurl))));
} else {
ret option::none;
}
};
ret option::none;
}

/**
* Parse a `str` to a `url`
*
* # Arguments
*
* `rawurl` - a string representing a full url, including scheme.
*
* # Returns
*
* a `url` that contains the parsed representation of the url.
*
*/

fn from_str(rawurl: str) -> result::result<url, str> {
let mut schm = get_scheme(rawurl);
if option::is_none(schm) {
ret result::err("invalid scheme");
}
let (scheme, rest) = option::unwrap(schm);
let (u, rest) = split_char_first(rest, '@');
let user = if str::len(rest) == 0 {
option::none
} else {
option::some(userinfo_from_str(u))
};
let rest = if str::len(rest) == 0 {
u
} else {
rest
};
let (rest, frag) = split_char_first(rest, '#');
let fragment = if str::len(frag) == 0 {
option::none
} else {
option::some(frag)
};
let (rest, query) = split_char_first(rest, '?');
let query = query_from_str(query);
let (host, pth) = split_char_first(rest, '/');
let mut path = pth;
if str::len(path) != 0 {
str::unshift_char(path, '/');
}

ret result::ok(url(scheme, user, host, path, query, fragment));
}

/**
* Format a `url` as a string
*
* # Arguments
*
* `url` - a url.
*
* # Returns
*
* a `str` that contains the formatted url. Note that this will usually
* be an inverse of `from_str` but might strip out unneeded separators.
* for example, "http://somehost.com?", when parsed and formatted, will
* result in just "http://somehost.com".
*
*/
fn to_str(url: url) -> str {
let user = if option::is_some(url.user) {
userinfo_to_str(option::unwrap(copy url.user))
} else {
""
};
let query = if url.query.size() == 0 {
""
} else {
str::concat(~["?", query_to_str(url.query)])
};
let fragment = if option::is_some(url.fragment) {
str::concat(~["#", option::unwrap(copy url.fragment)])
} else {
""
};

ret str::concat(~[copy url.scheme,
"://",
user,
copy url.host,
copy url.path,
query,
fragment]);
}

#[cfg(test)]
mod tests {
#[test]
fn test_full_url_parse_and_format() {
let url = "http://user:[email protected]/doc?s=v#something";
assert to_str(result::unwrap(from_str(url))) == url;
}

#[test]
fn test_userless_url_parse_and_format() {
let url = "http://rust-lang.org/doc?s=v#something";
assert to_str(result::unwrap(from_str(url))) == url;
}

#[test]
fn test_queryless_url_parse_and_format() {
let url = "http://user:[email protected]/doc#something";
assert to_str(result::unwrap(from_str(url))) == url;
}

#[test]
fn test_empty_query_url_parse_and_format() {
let url = "http://user:[email protected]/doc?#something";
let should_be = "http://user:[email protected]/doc#something";
assert to_str(result::unwrap(from_str(url))) == should_be;
}

#[test]
fn test_fragmentless_url_parse_and_format() {
let url = "http://user:[email protected]/doc?q=v";
assert to_str(result::unwrap(from_str(url))) == url;
}

#[test]
fn test_minimal_url_parse_and_format() {
let url = "http://rust-lang.org/doc";
assert to_str(result::unwrap(from_str(url))) == url;
}

#[test]
fn test_scheme_host_only_url_parse_and_format() {
let url = "http://rust-lang.org";
assert to_str(result::unwrap(from_str(url))) == url;
}

#[test]
fn test_pathless_url_parse_and_format() {
let url = "http://user:[email protected]?q=v#something";
assert to_str(result::unwrap(from_str(url))) == url;
}

#[test]
fn test_scheme_host_fragment_only_url_parse_and_format() {
let url = "http://rust-lang.org#something";
assert to_str(result::unwrap(from_str(url))) == url;
}

}
3 changes: 2 additions & 1 deletion src/libstd/std.rc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use core(vers = "0.2");
import core::*;

export net, net_tcp, net_ip;
export net, net_tcp, net_ip, net_url;
export uv, uv_ll, uv_iotask, uv_global_loop;
export c_vec, util, timer;
export bitv, deque, fun_treemap, list, map, smallintmap, sort, treemap;
Expand All @@ -30,6 +30,7 @@ export base64;
mod net;
mod net_ip;
mod net_tcp;
mod net_url;

// libuv modules
mod uv;
Expand Down