feat: URI merging and tests
This commit is contained in:
@@ -39,6 +39,67 @@ pub fn to_string(uri: Uri) -> String {
|
|||||||
string.concat(parts)
|
string.concat(parts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn merge(base: Uri, relative: Uri) -> Result(Uri, Nil) {
|
||||||
|
use <- bool.guard(when: base.scheme == None, return: Error(Nil))
|
||||||
|
let uri = case relative.scheme {
|
||||||
|
Some(_) -> {
|
||||||
|
Uri(..relative, path: remove_dot_segments(relative.path))
|
||||||
|
}
|
||||||
|
None -> {
|
||||||
|
let scheme = base.scheme
|
||||||
|
case relative.host, relative.port, relative.userinfo {
|
||||||
|
Some(_), _, _ | _, Some(_), _ | _, _, Some(_) -> {
|
||||||
|
Uri(..relative, scheme:, path: remove_dot_segments(relative.path))
|
||||||
|
}
|
||||||
|
_, _, _ -> {
|
||||||
|
case relative.path {
|
||||||
|
"" -> {
|
||||||
|
let query = case relative.query {
|
||||||
|
Some(_) -> relative.query
|
||||||
|
_ -> base.query
|
||||||
|
}
|
||||||
|
Uri(..base, query:)
|
||||||
|
}
|
||||||
|
"/" <> _ -> {
|
||||||
|
Uri(
|
||||||
|
..base,
|
||||||
|
path: remove_dot_segments(relative.path),
|
||||||
|
query: relative.query,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ -> {
|
||||||
|
let path = merge_paths(base, relative)
|
||||||
|
Uri(
|
||||||
|
..base,
|
||||||
|
path: remove_dot_segments(path),
|
||||||
|
query: relative.query,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri(..uri, fragment: relative.fragment) |> Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_authority(uri: Uri) -> Bool {
|
||||||
|
case uri.host, uri.userinfo, uri.port {
|
||||||
|
Some(_), _, _ | _, Some(_), _ | _, _, Some(_) -> True
|
||||||
|
_, _, _ -> False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_paths(base: Uri, relative: Uri) -> String {
|
||||||
|
case has_authority(base), base.path {
|
||||||
|
True, "" -> "/" <> relative.path
|
||||||
|
_, _ -> {
|
||||||
|
remove_segment(base.path) <> "/" <> relative.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn normalize(uri: Uri) -> Uri {
|
pub fn normalize(uri: Uri) -> Uri {
|
||||||
normalise(uri)
|
normalise(uri)
|
||||||
}
|
}
|
||||||
@@ -51,28 +112,28 @@ pub fn normalise(uri: Uri) -> Uri {
|
|||||||
let port = uri.port
|
let port = uri.port
|
||||||
let host =
|
let host =
|
||||||
uri.host |> option.map(string.lowercase) |> option.map(percent_normaliser)
|
uri.host |> option.map(string.lowercase) |> option.map(percent_normaliser)
|
||||||
let path = uri.path |> percent_normaliser |> normalise_path
|
let path = uri.path |> percent_normaliser |> remove_dot_segments
|
||||||
let query = uri.query |> option.map(percent_normaliser)
|
let query = uri.query |> option.map(percent_normaliser)
|
||||||
let fragment = uri.fragment |> option.map(percent_normaliser)
|
let fragment = uri.fragment |> option.map(percent_normaliser)
|
||||||
|
|
||||||
Uri(scheme, userinfo, host, port, path, query, fragment)
|
Uri(scheme, userinfo, host, port, path, query, fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalise_path(path: String) -> String {
|
fn remove_dot_segments(path: String) -> String {
|
||||||
do_normalise_path(path, "")
|
do_remove_dot_segments(path, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_normalise_path(path: String, acc: String) -> String {
|
fn do_remove_dot_segments(path: String, acc: String) -> String {
|
||||||
case path {
|
case path {
|
||||||
"../" <> rest | "./" <> rest -> do_normalise_path(rest, acc)
|
"../" <> rest | "./" <> rest -> do_remove_dot_segments(rest, acc)
|
||||||
"/./" <> rest -> do_normalise_path("/" <> rest, acc)
|
"/./" <> rest -> do_remove_dot_segments("/" <> rest, acc)
|
||||||
"/." -> do_normalise_path("/", acc)
|
"/." -> acc <> "/"
|
||||||
"/../" <> rest -> do_normalise_path("/" <> rest, remove_segment(acc))
|
"/../" <> rest -> do_remove_dot_segments("/" <> rest, remove_segment(acc))
|
||||||
"/.." -> do_normalise_path("/", remove_segment(acc))
|
"/.." -> remove_segment(acc) <> "/"
|
||||||
"." | ".." | "" -> acc
|
"." | ".." | "" -> acc
|
||||||
_ -> {
|
_ -> {
|
||||||
let assert Ok(#(char, rest)) = string.pop_grapheme(path)
|
let assert Ok(#(char, rest)) = string.pop_grapheme(path)
|
||||||
do_normalise_path(rest, acc <> char)
|
do_remove_dot_segments(rest, acc <> char)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -802,6 +802,99 @@ pub fn parse_failure_tests() {
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn merge_tests() {
|
||||||
|
describe("merging", [
|
||||||
|
it("relative merge", fn() {
|
||||||
|
let uri1 = uri.parse("/relative") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2) |> should.be_error
|
||||||
|
}),
|
||||||
|
it("simple merge", fn() {
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("http://example.com/baz") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2) |> should.equal(uri.parse("http://example.com/baz"))
|
||||||
|
}),
|
||||||
|
it("segments merge", fn() {
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl") |> should.be_ok
|
||||||
|
let uri2 =
|
||||||
|
uri.parse("http://example.com/.././bob/../../baz") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2) |> should.equal(uri.parse("http://example.com/baz"))
|
||||||
|
}),
|
||||||
|
it("base with authority merge", fn() {
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("//example.com/baz") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2) |> should.equal(uri.parse("http://example.com/baz"))
|
||||||
|
}),
|
||||||
|
it("base with authority segments merge", fn() {
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl") |> should.be_ok
|
||||||
|
let uri2 =
|
||||||
|
uri.parse("//example.com/.././bob/../../../baz") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2) |> should.equal(uri.parse("http://example.com/baz"))
|
||||||
|
}),
|
||||||
|
it("base with absolute merge", fn() {
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl/eh") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("/baz") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2) |> should.equal(uri.parse("http://google.com/baz"))
|
||||||
|
}),
|
||||||
|
it("base with relative merge", fn() {
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl/eh") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("baz") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2)
|
||||||
|
|> echo
|
||||||
|
|> should.equal(uri.parse("http://google.com/weebl/baz"))
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl/") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("baz") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2)
|
||||||
|
|> echo
|
||||||
|
|> should.equal(uri.parse("http://google.com/weebl/baz"))
|
||||||
|
let uri1 = uri.parse("http://google.com") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("baz") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2)
|
||||||
|
|> echo
|
||||||
|
|> should.equal(uri.parse("http://google.com/baz"))
|
||||||
|
}),
|
||||||
|
it("base with relative segments merge", fn() {
|
||||||
|
let uri1 = uri.parse("http://google.com") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("/.././bob/../../../baz") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2)
|
||||||
|
|> echo
|
||||||
|
|> should.equal(uri.parse("http://google.com/baz"))
|
||||||
|
}),
|
||||||
|
it("base with empty uri merge", fn() {
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl/bob") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2)
|
||||||
|
|> echo
|
||||||
|
|> should.equal(uri.parse("http://google.com/weebl/bob"))
|
||||||
|
}),
|
||||||
|
|
||||||
|
it("base with fragment merge", fn() {
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl/bob") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("#fragment") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2)
|
||||||
|
|> echo
|
||||||
|
|> should.equal(uri.parse("http://google.com/weebl/bob#fragment"))
|
||||||
|
}),
|
||||||
|
it("base with query merge", fn() {
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl/bob") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("?query") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2)
|
||||||
|
|> echo
|
||||||
|
|> should.equal(uri.parse("http://google.com/weebl/bob?query"))
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl/bob?query1") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("?query2") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2)
|
||||||
|
|> echo
|
||||||
|
|> should.equal(uri.parse("http://google.com/weebl/bob?query2"))
|
||||||
|
let uri1 = uri.parse("http://google.com/weebl/bob?query1") |> should.be_ok
|
||||||
|
let uri2 = uri.parse("") |> should.be_ok
|
||||||
|
uri.merge(uri1, uri2)
|
||||||
|
|> echo
|
||||||
|
|> should.equal(uri.parse("http://google.com/weebl/bob?query1"))
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
}
|
||||||
// gleeunit test functions end in `_test`
|
// gleeunit test functions end in `_test`
|
||||||
// pub fn uri_test() {
|
// pub fn uri_test() {
|
||||||
// match("uri:")
|
// match("uri:")
|
||||||
|
|||||||
Reference in New Issue
Block a user