Two grammars
One decoder, two standards. WHATWG normalises on the way in; RFC 3986 leaves the dots and the case alone.
URLs read the way a browser reads them and the way RFC 3986 says to read them: one type, two grammars. Host names are internationalised, URI templates fill in and read back, and form bodies ride on the same percent coding. No sockets.
Switch the reader for a single string and keep working with the result.
UTS 46 mapping and RFC 3492 punycode turn any script into ASCII.
RFC 6570, all four levels: expand a URI and read one back.
application/x-www-form-urlencoded as ordered, repeatable name/value pairs.
RFC 2397 — a whole representation written inside the reference to it.
Escape and unescape by component, with the safe set each one allows.
moon add moonbitstack/moonurl
Each snippet runs in a project that has only this package added.
A reference is five components. Read it, then ask for any of them.
let url = @moonurl.Url::decode("https://leo:pw@example.test:8443/a/b?x=1#top")
url.scheme // Some("https")
url.host // Some("example.test")
url.port // Some(8443)
url.query // Some("x=1")
url.origin() // "https://example.test:8443"
resolve applies RFC 3986 §5.4 against a base.
let base = @moonurl.Url::decode("http://example.test/a/b/c")
base.resolve(@moonurl.Url::decode("../d"))
// http://example.test/a/d
syntax defaults to rfc3986, so existing code is unchanged. The WHATWG reading normalises on the way in.
@moonurl.Url::decode("HTTP://EXAMPLE.test:80/a/../b").encode()
// "HTTP://EXAMPLE.test:80/a/../b"
@moonurl.Url::decode("HTTP://EXAMPLE.test:80/a/../b", syntax=@moonurl.Whatwg).encode()
// "http://example.test/b"
A host name goes through UTS 46; an ASCII name is taken as it stands.
@moonurl.domain("MÜNCHEN.example") // "xn--mnchen-3ya.example"
@moonurl.Url::decode("http://192.0x00A80001/", syntax=@moonurl.Whatwg).host
// Some("192.168.0.1")
All four levels; capture reverses expand.
let values : Map[String, @template.Value] = Map([])
values.set("id", @template.Value::Text("42"))
values.set("page", @template.Value::Text("2"))
@template.Template::decode("/users/{id}{?page}").expand(values)
// "/users/42?page=2"
@template.Template::decode("/users/{id}{?page}").capture("/users/42?page=2")
// [("id", "42"), ("page", "2")]
A URL's query and a form body are the same ordered pairs.
let q = @moonurl.Query::decode("a=1&b=2&a=3")
q.get("a") // Some("1")
q.values("a") // ["1", "3"]
q.encode() // "a=1&a=3&b=2"
@moonurl.dataurl.Url::decode("data:text/plain,Hi%20there").data
// b"Hi there"
moon run src/examples/tour
Source on GitHub ↗
Public items, grouped by package. Signatures match the compiled interface.
syntax defaults to rfc3986; base resolves a relative reference.scheme://host:port, or "null".. and .. segments.+ is a space; serialiser keeps only letters, digits and *-._.is_ok() tells you; errors lists what the standard objected to.Variables works; a Map[String, Value] already does.Refused — every offset named at.
Source on GitHub ↗
The same input, read several ways — then the tools to try your own.
One decoder, two standards. WHATWG normalises on the way in; RFC 3986 leaves the dots and the case alone.
UTS 46 maps and normalises; RFC 3492 punycode shrinks it to ASCII that DNS understands.
Fill a hole, read it back. expand and capture are exact inverses.
Type a host name. Non-ASCII labels become xn-- under UTS 46.
Give a template and variables as JSON. Lists and objects become lists and pairs.
The same string by either grammar: RFC 3986, or the WHATWG URL Standard a browser follows.