feat: Refactor and added normali(s/z)e

This commit is contained in:
2025-09-07 16:35:20 +01:00
parent 9d2625f9b6
commit b7325de6ec
6 changed files with 1197 additions and 948 deletions

View File

@@ -14,6 +14,7 @@ version = "1.0.0"
[dependencies] [dependencies]
gleam_stdlib = ">= 0.44.0 and < 2.0.0" gleam_stdlib = ">= 0.44.0 and < 2.0.0"
splitter = ">= 1.1.0 and < 2.0.0"
[dev-dependencies] [dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0" gleeunit = ">= 1.0.0 and < 2.0.0"

View File

@@ -22,6 +22,7 @@ packages = [
{ name = "ranger", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_yielder"], otp_app = "ranger", source = "hex", outer_checksum = "C8988E8F8CDBD3E7F4D8F2E663EF76490390899C2B2885A6432E942495B3E854" }, { name = "ranger", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_yielder"], otp_app = "ranger", source = "hex", outer_checksum = "C8988E8F8CDBD3E7F4D8F2E663EF76490390899C2B2885A6432E942495B3E854" },
{ name = "simplifile", version = "2.3.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0A868DAC6063D9E983477981839810DC2E553285AB4588B87E3E9C96A7FB4CB4" }, { name = "simplifile", version = "2.3.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0A868DAC6063D9E983477981839810DC2E553285AB4588B87E3E9C96A7FB4CB4" },
{ name = "snag", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "7E9F06390040EB5FAB392CE642771484136F2EC103A92AE11BA898C8167E6E17" }, { name = "snag", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "7E9F06390040EB5FAB392CE642771484136F2EC103A92AE11BA898C8167E6E17" },
{ name = "splitter", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "05564A381580395DCDEFF4F88A64B021E8DAFA6540AE99B4623962F52976AA9D" },
{ name = "startest", version = "0.7.0", build_tools = ["gleam"], requirements = ["argv", "bigben", "birl", "exception", "gleam_community_ansi", "gleam_erlang", "gleam_javascript", "gleam_regexp", "gleam_stdlib", "glint", "simplifile", "tom"], otp_app = "startest", source = "hex", outer_checksum = "71B9CB82C4B8779A4BD54C7151DF7D0B0F778D0DDE805B782B44EFA7BA8F50DA" }, { name = "startest", version = "0.7.0", build_tools = ["gleam"], requirements = ["argv", "bigben", "birl", "exception", "gleam_community_ansi", "gleam_erlang", "gleam_javascript", "gleam_regexp", "gleam_stdlib", "glint", "simplifile", "tom"], otp_app = "startest", source = "hex", outer_checksum = "71B9CB82C4B8779A4BD54C7151DF7D0B0F778D0DDE805B782B44EFA7BA8F50DA" },
{ name = "tom", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_time"], otp_app = "tom", source = "hex", outer_checksum = "74D0C5A3761F7A7D06994755D4D5AD854122EF8E9F9F76A3E7547606D8C77091" }, { name = "tom", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_time"], otp_app = "tom", source = "hex", outer_checksum = "74D0C5A3761F7A7D06994755D4D5AD854122EF8E9F9F76A3E7547606D8C77091" },
] ]
@@ -29,4 +30,5 @@ packages = [
[requirements] [requirements]
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" } gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
splitter = { version = ">= 1.1.0 and < 2.0.0" }
startest = { version = ">= 0.7.0 and < 1.0.0" } startest = { version = ">= 0.7.0 and < 1.0.0" }

850
src/internal/parser.gleam Normal file
View File

@@ -0,0 +1,850 @@
import gleam/bool
import gleam/int
import gleam/list.{Continue, Stop}
import gleam/option.{None, Some}
import gleam/result
import gleam/string
import types.{type Uri, Uri, empty_uri}
pub fn parse(uri: String) -> Result(Uri, Nil) {
case parse_scheme(uri) {
Ok(#(scheme, rest)) -> {
use #(rel_part, rest) <- result.try(parse_hier_part(rest))
use #(query, rest) <- result.try(parse_query(rest))
use #(fragment, rest) <- result.try(parse_fragment(rest))
case rest {
"" -> Ok(combine_uris([scheme, rel_part, query, fragment]))
_ -> Error(Nil)
}
}
Error(_) -> {
use #(rel_part, rest) <- result.try(parse_relative_part(uri))
use #(query, rest) <- result.try(parse_query(rest))
use #(fragment, rest) <- result.try(parse_fragment(rest))
case rest {
"" -> Ok(combine_uris([rel_part, query, fragment]))
_ -> Error(Nil)
}
}
}
}
fn parse_query(str: String) -> Result(#(Uri, String), Nil) {
case str {
"?" <> rest -> {
let #(query, rest) = get_multiple_optional(parse_query_fragment, rest)
Ok(#(Uri(..empty_uri, query: Some(query)), rest))
}
_ -> Ok(#(empty_uri, str))
}
}
fn parse_fragment(str: String) -> Result(#(Uri, String), Nil) {
case str {
"#" <> rest -> {
let #(fragment, rest) = get_multiple_optional(parse_query_fragment, rest)
Ok(#(Uri(..empty_uri, fragment: Some(fragment)), rest))
}
_ -> Ok(#(empty_uri, str))
}
}
fn parse_hier_part(str: String) -> Result(#(Uri, String), Nil) {
list.fold_until(
[parse_authority, parse_absolute, parse_rootless, parse_empty],
Error(Nil),
get_parser_fn(str),
)
}
fn parse_relative_part(str: String) -> Result(#(Uri, String), Nil) {
list.fold_until(
[parse_authority, parse_absolute, parse_noscheme, parse_empty],
Error(Nil),
get_parser_fn(str),
)
}
fn parse_absolute(str: String) -> Result(#(Uri, String), Nil) {
case str {
"/" <> rest -> {
let assert Ok(#(seg, rest)) =
parse_optional(rest, parse_this_then(
[
do_parse_segment_nz,
get_multiple_optional_result(
fn(str) {
case str {
"/" <> rest -> {
do_parse_segment(rest, do_parse_pchar, "/")
}
_ -> Error(Nil)
}
},
_,
),
],
_,
))
Ok(#(Uri(None, None, None, None, "/" <> seg, None, None), rest))
}
_ -> Error(Nil)
}
}
fn parse_rootless(str: String) -> Result(#(Uri, String), Nil) {
use #(seg1, rest) <- result.try(do_parse_segment_nz(str))
let #(segs, rest) =
get_multiple_optional(
fn(str) {
case str {
"/" <> rest -> {
do_parse_segment(rest, do_parse_pchar, "/")
}
_ -> Error(Nil)
}
},
rest,
)
Ok(#(Uri(None, None, None, None, seg1 <> segs, None, None), rest))
}
fn parse_noscheme(str: String) -> Result(#(Uri, String), Nil) {
use #(seg1, rest) <- result.try(do_parse_segment_nz_nc(str))
let #(segs, rest) =
get_multiple_optional(
fn(str) {
case str {
"/" <> rest -> {
do_parse_segment(rest, do_parse_pchar, "/")
}
_ -> Error(Nil)
}
},
rest,
)
Ok(#(Uri(None, None, None, None, seg1 <> segs, None, None), rest))
}
fn parse_optional(str, opt_fn) {
case opt_fn(str) {
Error(Nil) -> Ok(#("", str))
Ok(r) -> Ok(r)
}
}
fn get_multiple_optional_result(opt_fn, str: String) {
get_multiple_optional(opt_fn, str) |> Ok
}
fn get_multiple_optional(opt_fn, str: String) {
case get_multiple(opt_fn, str) {
Error(_) -> #("", str)
Ok(r) -> r
}
}
fn parse_empty(str: String) -> Result(#(Uri, String), Nil) {
Ok(#(Uri(None, None, None, None, "", None, None), str))
}
fn parse_authority(str: String) -> Result(#(Uri, String), Nil) {
case str {
"//" <> rest -> {
parse_authority_part(rest) |> echo
}
_ -> Error(Nil)
}
}
fn parse_authority_part(str: String) -> Result(#(Uri, String), Nil) {
let #(ui, rest) = case parse_userinfo(str, "") {
Ok(#(ui, rest)) -> #(Some(ui), rest)
Error(_) -> #(None, str)
}
use #(host, rest) <- result.try(parse_host(rest))
let #(port, rest) = case parse_port(rest) {
Ok(#("", rest)) -> #(None, rest)
Error(_) -> #(None, rest)
Ok(#(port, rest)) -> {
let assert Ok(port) = int.parse(port)
#(Some(port), rest)
}
}
let #(path, rest) = parse_abs_empty(rest)
Ok(#(Uri(None, ui, Some(host), port, path, None, None), rest))
}
fn parse_port(str: String) {
case str {
":" <> rest -> {
Ok(parse_digits(rest, ""))
}
_ -> Error(Nil)
}
}
fn parse_digits(str: String, digits: String) {
case parse_digit(str) {
Ok(#(d, rest)) -> {
parse_digits(rest, digits <> d)
}
Error(_) -> #(digits, str)
}
}
fn parse_host(str: String) {
list.fold_until(
[parse_ip_literal, parse_ipv4, parse_reg_name],
Error(Nil),
get_parser_fn(str),
)
}
fn parse_ip_literal(str: String) {
case str {
"[" <> rest -> {
use #(ip, rest) <- result.try(list.fold_until(
[parse_ipv6, parse_ipfuture],
Error(Nil),
get_parser_fn(rest),
))
case rest {
"]" <> rest -> Ok(#(ip, rest))
_ -> Error(Nil)
}
}
_ -> Error(Nil)
}
}
fn parse_ipv6(str: String) {
list.fold_until(
[
parse_this_then([parse_min_max(_, 6, 6, parse_h16_colon), parse_ls32], _),
parse_this_then(
[parse_colons, parse_min_max(_, 5, 5, parse_h16_colon), parse_ls32],
_,
),
parse_this_then(
[
parse_optional(_, parse_h16),
parse_colons,
parse_min_max(_, 4, 4, parse_h16_colon),
parse_ls32,
],
_,
),
parse_this_then(
[
parse_optional(_, parse_this_then([parse_h16s(_, 1), parse_h16], _)),
parse_colons,
parse_min_max(_, 3, 3, parse_h16_colon),
parse_ls32,
],
_,
),
parse_this_then(
[
parse_optional(_, parse_this_then([parse_h16s(_, 2), parse_h16], _)),
parse_colons,
parse_min_max(_, 2, 2, parse_h16_colon),
parse_ls32,
],
_,
),
parse_this_then(
[
parse_optional(_, parse_this_then([parse_h16s(_, 3), parse_h16], _)),
parse_colons,
parse_min_max(_, 1, 1, parse_h16_colon),
parse_ls32,
],
_,
),
parse_this_then(
[
parse_optional(_, parse_this_then([parse_h16s(_, 4), parse_h16], _)),
parse_colons,
parse_ls32,
],
_,
),
parse_this_then(
[
parse_optional(_, parse_this_then([parse_h16s(_, 5), parse_h16], _)),
parse_colons,
parse_h16,
],
_,
),
parse_this_then(
[
parse_optional(_, parse_this_then([parse_h16s(_, 6), parse_h16], _)),
parse_colons,
],
_,
),
],
Error(Nil),
get_parser_fn(str),
)
}
fn parse_h16s(str: String, max) {
parse_min_max(str, 0, max, parse_h16_colon)
}
fn parse_colons(str: String) {
case str {
"::" <> rest -> Ok(#("::", rest))
_ -> Error(Nil)
}
}
fn parse_this_then(
parsers: List(fn(String) -> Result(#(String, String), Nil)),
str: String,
) {
list.fold_until(parsers, Ok(#("", str)), fn(acc, parser) {
let assert Ok(#(res, str)) = acc
case parser(str) {
Ok(#(res2, rest)) -> {
Continue(Ok(#(res <> res2, rest)))
}
Error(Nil) -> Stop(Error(Nil))
}
})
}
fn parse_ls32(str: String) -> Result(#(String, String), Nil) {
list.fold_until([parse_h16_pair, parse_ipv4], Error(Nil), get_parser_fn(str))
}
fn parse_h16_pair(str: String) {
use #(h16a, rest) <- result.try(parse_h16(str))
case rest {
":" <> rest -> {
use #(h16b, rest) <- result.try(parse_h16(rest))
Ok(#(h16a <> ":" <> h16b, rest))
}
_ -> Error(Nil)
}
}
fn parse_h16(str: String) {
parse_hex_digits(str, 1, 4)
}
fn parse_h16_colon(str: String) {
use #(h16, rest) <- result.try(parse_h16(str))
case rest {
":" <> rest -> Ok(#(h16 <> ":", rest))
_ -> Error(Nil)
}
}
fn parse_ipfuture(str: String) {
Error(Nil)
}
fn get_multiple(
to_run: fn(String) -> Result(#(String, String), Nil),
str: String,
) -> Result(#(String, String), Nil) {
case do_get_multiple(to_run, str, "") {
Ok(#("", _)) | Error(Nil) -> Error(Nil)
Ok(#(r, rest)) -> Ok(#(r, rest))
}
}
fn do_get_multiple(
to_run: fn(String) -> Result(#(String, String), Nil),
str: String,
ret: String,
) -> Result(#(String, String), Nil) {
case str {
"" -> Ok(#(ret, str))
_ ->
case to_run(str) {
Ok(#(r, rest)) -> do_get_multiple(to_run, rest, ret <> r)
Error(_) -> Ok(#(ret, str))
}
}
}
fn parse_query_fragment(str: String) {
list.fold_until(
[
do_parse_pchar,
fn(str: String) {
case str {
"/" as l <> rest | "?" as l <> rest -> Ok(#(l, rest))
_ -> Error(Nil)
}
},
],
Error(Nil),
get_parser_fn(str),
)
}
fn parse_abs_empty(str: String) -> #(String, String) {
get_multiple_optional(
fn(str) {
case str {
"/" <> rest -> {
do_parse_segment(rest, do_parse_pchar, "/")
}
_ -> Error(Nil)
}
},
str,
)
}
fn do_parse_segment(
str: String,
char_fn,
segment: String,
) -> Result(#(String, String), Nil) {
case char_fn(str) {
Error(Nil) | Ok(#("", _)) -> Ok(#(segment, str))
Ok(#(l, rest)) -> do_parse_segment(rest, char_fn, segment <> l)
}
}
fn do_parse_segment_nz(str: String) {
use #(char1, rest) <- result.try(do_parse_pchar(str))
use #(chars, rest) <- result.try(do_parse_segment(rest, do_parse_pchar, char1))
Ok(#(chars, rest))
}
fn do_parse_segment_nz_nc(str: String) {
use #(char1, rest) <- result.try(do_parse_pchar_nc(str))
use #(chars, rest) <- result.try(do_parse_segment(
rest,
do_parse_pchar_nc,
char1,
))
Ok(#(chars, rest))
}
fn do_parse_pchar(str: String) {
list.fold_until(
[
parse_unreserved,
parse_pct_encoded,
parse_sub_delim,
fn(str: String) {
case str {
":" as l <> rest | "@" as l <> rest -> Ok(#(l, rest))
_ -> Error(Nil)
}
},
],
Error(Nil),
get_parser_fn(str),
)
}
fn do_parse_pchar_nc(str: String) {
list.fold_until(
[
parse_unreserved,
parse_pct_encoded,
parse_sub_delim,
fn(str: String) {
case str {
"@" as l <> rest -> Ok(#(l, rest))
_ -> Error(Nil)
}
},
],
Error(Nil),
get_parser_fn(str),
)
}
fn parse_reg_name(str: String) {
// can't error
case do_parse_reg_name(str, "") |> echo {
Error(Nil) -> Ok(#("", str))
Ok(#(reg_name, rest)) -> Ok(#(reg_name, rest))
}
}
fn do_parse_reg_name(str: String, reg_name: String) {
case
list.fold_until(
[parse_unreserved, parse_pct_encoded, parse_sub_delim],
Error(Nil),
get_parser_fn(str),
)
{
Error(Nil) | Ok(#("", _)) -> Ok(#(reg_name, str))
Ok(#(l, rest)) -> do_parse_reg_name(rest, reg_name <> l)
}
}
fn parse_pct_encoded(str: String) {
case str {
"%" <> rest -> {
use #(hex1, rest) <- result.try(parse_hex_digit(rest))
use #(hex2, rest) <- result.try(parse_hex_digit(rest))
Ok(#("%" <> hex1 <> hex2, rest))
}
_ -> Error(Nil)
}
}
fn parse_sub_delim(str: String) {
case str {
"!" as l <> rest
| "$" as l <> rest
| "&" as l <> rest
| "'" as l <> rest
| "(" as l <> rest
| ")" as l <> rest
| "*" as l <> rest
| "+" as l <> rest
| "," as l <> rest
| ";" as l <> rest
| "=" as l <> rest -> Ok(#(l, rest))
_ -> Error(Nil)
}
}
fn parse_ipv4(str: String) {
use #(oct1, rest) <- result.try(parse_dec_octet(str))
use rest <- result.try(case rest {
"." <> rest -> Ok(rest)
_ -> Error(Nil)
})
use #(oct2, rest) <- result.try(parse_dec_octet(rest))
use rest <- result.try(case rest {
"." <> rest -> Ok(rest)
_ -> Error(Nil)
})
use #(oct3, rest) <- result.try(parse_dec_octet(rest))
use rest <- result.try(case rest {
"." <> rest -> Ok(rest)
_ -> Error(Nil)
})
use #(oct4, rest) <- result.try(parse_dec_octet(rest))
Ok(#(oct1 <> "." <> oct2 <> "." <> oct3 <> "." <> oct4, rest))
}
fn parse_dec_octet(str: String) -> Result(#(String, String), Nil) {
let matches = [
["2", "5", "012345"],
["2", "01234", "0123456789"],
["1", "0123456789", "0123456789"],
["123456789", "0123456789"],
["0123456789"],
]
list.fold_until(matches, Error(Nil), fn(_, chars) {
case
list.fold_until(chars, #("", str), fn(acc, charset) {
let #(octet, str) = acc
case string.pop_grapheme(str) {
Error(_) -> Stop(#("", ""))
Ok(#(char, rest)) -> {
case string.contains(charset, char) {
True -> Continue(#(octet <> char, rest))
False -> Stop(#("", ""))
}
}
}
})
{
#("", _) -> Continue(Error(Nil))
#(octet, rest) -> Stop(Ok(#(octet, rest)))
}
})
}
fn parse_userinfo(
str: String,
userinfo: String,
) -> Result(#(String, String), Nil) {
case str {
"@" <> rest -> Ok(#(userinfo, rest))
"" -> Error(Nil)
_ -> {
use #(part, rest) <- result.try(list.fold_until(
[
parse_unreserved,
parse_pct_encoded,
parse_sub_delim,
fn(str: String) {
case str {
":" as l <> rest -> Ok(#(l, rest))
_ -> Error(Nil)
}
},
],
Error(Nil),
get_parser_fn(str),
))
parse_userinfo(rest, userinfo <> part)
}
}
}
fn parse_scheme(str: String) -> Result(#(Uri, String), Nil) {
case parse_alpha(str) {
Ok(#(first, rest)) -> {
case do_parse_scheme(rest, first) {
Error(_) -> Error(Nil)
Ok(#(scheme, rest)) ->
Ok(#(Uri(Some(scheme), None, None, None, "", None, None), rest))
}
}
_ -> Error(Nil)
}
}
fn do_parse_scheme(
str: String,
scheme: String,
) -> Result(#(String, String), Nil) {
case str {
":" <> rest -> Ok(#(scheme, rest))
"" -> Error(Nil)
_ -> {
use #(part, rest) <- result.try(list.fold_until(
[
parse_alpha,
parse_digit,
fn(str) {
case str {
"+" as l <> rest | "-" as l <> rest | "." as l <> rest ->
Ok(#(l, rest))
_ -> Error(Nil)
}
},
],
Error(Nil),
get_parser_fn(str),
))
do_parse_scheme(rest, scheme <> part)
}
}
}
fn get_parser_fn(
str: String,
) -> fn(a, fn(String) -> Result(b, c)) -> list.ContinueOrStop(Result(b, Nil)) {
fn(_, parse_fn) {
case parse_fn(str) {
Ok(r) -> Stop(Ok(r))
Error(_) -> Continue(Error(Nil))
}
}
}
fn parse_min_max(str, min, max, parse_fn) {
use <- bool.guard(when: min < 0 || max <= 0 || min > max, return: Error(Nil))
case
list.repeat("", max)
|> list.fold_until(Ok(#("", str, 0)), fn(acc, _) {
let assert Ok(#(hex, str, i)) = acc
case parse_fn(str) {
Error(_) ->
case i < min {
True -> Stop(Error(Nil))
False -> Stop(Ok(#(hex, str, i)))
}
Ok(#(l, rest)) -> Continue(Ok(#(hex <> l, rest, i + 1)))
}
})
{
Error(_) -> Error(Nil)
Ok(#(hex, str, _)) -> Ok(#(hex, str))
}
}
fn parse_hex_digits(str, min, max) {
parse_min_max(str, min, max, parse_hex_digit)
}
pub fn parse_hex_digit(str) {
case str {
"0" as l <> rest
| "1" as l <> rest
| "2" as l <> rest
| "3" as l <> rest
| "4" as l <> rest
| "5" as l <> rest
| "6" as l <> rest
| "7" as l <> rest
| "8" as l <> rest
| "9" as l <> rest
| "a" as l <> rest
| "b" as l <> rest
| "c" as l <> rest
| "d" as l <> rest
| "e" as l <> rest
| "f" as l <> rest
| "A" as l <> rest
| "B" as l <> rest
| "C" as l <> rest
| "D" as l <> rest
| "E" as l <> rest
| "F" as l <> rest -> Ok(#(l, rest))
_ -> Error(Nil)
}
}
fn parse_digit(str: String) -> Result(#(String, String), Nil) {
case str {
"0" as l <> rest
| "1" as l <> rest
| "2" as l <> rest
| "3" as l <> rest
| "4" as l <> rest
| "5" as l <> rest
| "6" as l <> rest
| "7" as l <> rest
| "8" as l <> rest
| "9" as l <> rest -> Ok(#(l, rest))
_ -> Error(Nil)
}
}
fn parse_alpha(str: String) -> Result(#(String, String), Nil) {
case str {
"a" as l <> rest
| "b" as l <> rest
| "c" as l <> rest
| "d" as l <> rest
| "e" as l <> rest
| "f" as l <> rest
| "g" as l <> rest
| "h" as l <> rest
| "i" as l <> rest
| "j" as l <> rest
| "k" as l <> rest
| "l" as l <> rest
| "m" as l <> rest
| "n" as l <> rest
| "o" as l <> rest
| "p" as l <> rest
| "q" as l <> rest
| "r" as l <> rest
| "s" as l <> rest
| "t" as l <> rest
| "u" as l <> rest
| "v" as l <> rest
| "w" as l <> rest
| "x" as l <> rest
| "y" as l <> rest
| "z" as l <> rest
| "A" as l <> rest
| "B" as l <> rest
| "C" as l <> rest
| "D" as l <> rest
| "E" as l <> rest
| "F" as l <> rest
| "G" as l <> rest
| "H" as l <> rest
| "I" as l <> rest
| "J" as l <> rest
| "K" as l <> rest
| "L" as l <> rest
| "M" as l <> rest
| "N" as l <> rest
| "O" as l <> rest
| "P" as l <> rest
| "Q" as l <> rest
| "R" as l <> rest
| "S" as l <> rest
| "T" as l <> rest
| "U" as l <> rest
| "V" as l <> rest
| "W" as l <> rest
| "X" as l <> rest
| "Y" as l <> rest
| "Z" as l <> rest -> Ok(#(l, rest))
_ -> Error(Nil)
}
}
fn parse_unreserved(str: String) -> Result(#(String, String), Nil) {
list.fold_until(
[
parse_alpha,
parse_digit,
fn(str) {
case str {
"_" as l <> rest
| "-" as l <> rest
| "." as l <> rest
| "~" as l <> rest -> Ok(#(l, rest))
_ -> Error(Nil)
}
},
],
Error(Nil),
get_parser_fn(str),
)
}
fn combine_uris(uris: List(Uri)) -> Uri {
list.fold(uris, Uri(None, None, None, None, "", None, None), fn(acc, uri) {
let acc = case uri {
Uri(Some(scheme), _, _, _, _, _, _) -> Uri(..acc, scheme: Some(scheme))
_ -> acc
}
let acc = case uri {
Uri(_, Some(userinfo), _, _, _, _, _) ->
Uri(..acc, userinfo: Some(userinfo))
_ -> acc
}
let acc = case uri {
Uri(_, _, Some(host), _, _, _, _) -> Uri(..acc, host: Some(host))
_ -> acc
}
let acc = case uri {
Uri(_, _, _, Some(port), _, _, _) -> Uri(..acc, port: Some(port))
_ -> acc
}
let acc = case uri {
Uri(_, _, _, _, path, _, _) if path != "" -> Uri(..acc, path: path)
_ -> acc
}
let acc = case uri {
Uri(_, _, _, _, _, Some(query), _) -> Uri(..acc, query: Some(query))
_ -> acc
}
case uri {
Uri(_, _, _, _, _, _, Some(fragment)) ->
Uri(..acc, fragment: Some(fragment))
_ -> acc
}
})
}

15
src/types.gleam Normal file
View File

@@ -0,0 +1,15 @@
import gleam/option.{type Option, None}
pub type Uri {
Uri(
scheme: Option(String),
userinfo: Option(String),
host: Option(String),
port: Option(Int),
path: String,
query: Option(String),
fragment: Option(String),
)
}
pub const empty_uri = Uri(None, None, None, None, "", None, None)

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,8 @@
import gleam/option.{None, Some} import gleam/option.{None, Some}
import gleeunit/should import gleeunit/should
import startest.{describe, it} import startest.{describe, it}
import uri.{Uri} import types.{Uri}
import uri
pub fn main() { pub fn main() {
startest.run(startest.default_config()) startest.run(startest.default_config())
@@ -10,26 +11,26 @@ pub fn main() {
pub fn parse_scheme_tests() { pub fn parse_scheme_tests() {
describe("scheme parsing", [ describe("scheme parsing", [
it("simple parse", fn() { it("simple parse", fn() {
uri.parse("") |> should.equal(Ok(uri.empty_uri)) uri.parse("") |> should.equal(Ok(types.empty_uri))
uri.parse("foo") uri.parse("foo")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "foo"))) |> should.equal(Ok(Uri(..types.empty_uri, path: "foo")))
uri.parse("foo:") uri.parse("foo:")
|> should.equal(Ok(Uri(..uri.empty_uri, scheme: Some("foo")))) |> should.equal(Ok(Uri(..types.empty_uri, scheme: Some("foo"))))
uri.parse("foo:bar:nisse") uri.parse("foo:bar:nisse")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), path: "bar:nisse"), Uri(..types.empty_uri, scheme: Some("foo"), path: "bar:nisse"),
)) ))
uri.parse("foo://") uri.parse("foo://")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), host: Some("")), Uri(..types.empty_uri, scheme: Some("foo"), host: Some("")),
)) ))
uri.parse("foo:///") uri.parse("foo:///")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), host: Some(""), path: "/"), Uri(..types.empty_uri, scheme: Some("foo"), host: Some(""), path: "/"),
)) ))
uri.parse("foo:////") uri.parse("foo:////")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), host: Some(""), path: "//"), Uri(..types.empty_uri, scheme: Some("foo"), host: Some(""), path: "//"),
)) ))
}), }),
]) ])
@@ -40,34 +41,34 @@ pub fn parse_userinfo_tests() {
it("simple parse", fn() { it("simple parse", fn() {
uri.parse("user:password@localhost") uri.parse("user:password@localhost")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("user"), path: "password@localhost"), Uri(..types.empty_uri, scheme: Some("user"), path: "password@localhost"),
)) ))
uri.parse("user@") uri.parse("user@")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "user@"))) |> should.equal(Ok(Uri(..types.empty_uri, path: "user@")))
uri.parse("/user@") uri.parse("/user@")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "/user@"))) |> should.equal(Ok(Uri(..types.empty_uri, path: "/user@")))
uri.parse("user@localhost") uri.parse("user@localhost")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "user@localhost"))) |> should.equal(Ok(Uri(..types.empty_uri, path: "user@localhost")))
uri.parse("//user@localhost") uri.parse("//user@localhost")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, userinfo: Some("user"), host: Some("localhost")), Uri(..types.empty_uri, userinfo: Some("user"), host: Some("localhost")),
)) ))
uri.parse("//user:password@localhost") uri.parse("//user:password@localhost")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
userinfo: Some("user:password"), userinfo: Some("user:password"),
host: Some("localhost"), host: Some("localhost"),
), ),
)) ))
uri.parse("foo:/user@") uri.parse("foo:/user@")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), path: "/user@"), Uri(..types.empty_uri, scheme: Some("foo"), path: "/user@"),
)) ))
uri.parse("foo://user@localhost") uri.parse("foo://user@localhost")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
userinfo: Some("user"), userinfo: Some("user"),
host: Some("localhost"), host: Some("localhost"),
@@ -76,7 +77,7 @@ pub fn parse_userinfo_tests() {
uri.parse("foo://user:password@localhost") uri.parse("foo://user:password@localhost")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
userinfo: Some("user:password"), userinfo: Some("user:password"),
host: Some("localhost"), host: Some("localhost"),
@@ -87,27 +88,27 @@ pub fn parse_userinfo_tests() {
uri.parse("user:%E5%90%88@%E6%B0%97%E9%81%93") uri.parse("user:%E5%90%88@%E6%B0%97%E9%81%93")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("user"), scheme: Some("user"),
path: "%E5%90%88@%E6%B0%97%E9%81%93", path: "%E5%90%88@%E6%B0%97%E9%81%93",
), ),
)) ))
uri.parse("%E5%90%88%E6%B0%97%E9%81%93@") uri.parse("%E5%90%88%E6%B0%97%E9%81%93@")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, path: "%E5%90%88%E6%B0%97%E9%81%93@"), Uri(..types.empty_uri, path: "%E5%90%88%E6%B0%97%E9%81%93@"),
)) ))
uri.parse("/%E5%90%88%E6%B0%97%E9%81%93@") uri.parse("/%E5%90%88%E6%B0%97%E9%81%93@")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, path: "/%E5%90%88%E6%B0%97%E9%81%93@"), Uri(..types.empty_uri, path: "/%E5%90%88%E6%B0%97%E9%81%93@"),
)) ))
uri.parse("%E5%90%88@%E6%B0%97%E9%81%93") uri.parse("%E5%90%88@%E6%B0%97%E9%81%93")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, path: "%E5%90%88@%E6%B0%97%E9%81%93"), Uri(..types.empty_uri, path: "%E5%90%88@%E6%B0%97%E9%81%93"),
)) ))
uri.parse("//%E5%90%88@%E6%B0%97%E9%81%93") uri.parse("//%E5%90%88@%E6%B0%97%E9%81%93")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("%E6%B0%97%E9%81%93"), host: Some("%E6%B0%97%E9%81%93"),
userinfo: Some("%E5%90%88"), userinfo: Some("%E5%90%88"),
), ),
@@ -115,7 +116,7 @@ pub fn parse_userinfo_tests() {
uri.parse("//%E5%90%88:%E6%B0%97@%E9%81%93") uri.parse("//%E5%90%88:%E6%B0%97@%E9%81%93")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("%E9%81%93"), host: Some("%E9%81%93"),
userinfo: Some("%E5%90%88:%E6%B0%97"), userinfo: Some("%E5%90%88:%E6%B0%97"),
), ),
@@ -123,7 +124,7 @@ pub fn parse_userinfo_tests() {
uri.parse("foo:/%E5%90%88%E6%B0%97%E9%81%93@") uri.parse("foo:/%E5%90%88%E6%B0%97%E9%81%93@")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
path: "/%E5%90%88%E6%B0%97%E9%81%93@", path: "/%E5%90%88%E6%B0%97%E9%81%93@",
), ),
@@ -131,7 +132,7 @@ pub fn parse_userinfo_tests() {
uri.parse("foo://%E5%90%88@%E6%B0%97%E9%81%93") uri.parse("foo://%E5%90%88@%E6%B0%97%E9%81%93")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
userinfo: Some("%E5%90%88"), userinfo: Some("%E5%90%88"),
host: Some("%E6%B0%97%E9%81%93"), host: Some("%E6%B0%97%E9%81%93"),
@@ -140,7 +141,7 @@ pub fn parse_userinfo_tests() {
uri.parse("foo://%E5%90%88:%E6%B0%97@%E9%81%93") uri.parse("foo://%E5%90%88:%E6%B0%97@%E9%81%93")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
userinfo: Some("%E5%90%88:%E6%B0%97"), userinfo: Some("%E5%90%88:%E6%B0%97"),
host: Some("%E9%81%93"), host: Some("%E9%81%93"),
@@ -156,15 +157,15 @@ pub fn parse_host_tests() {
describe("host parsing", [ describe("host parsing", [
it("simple parse", fn() { it("simple parse", fn() {
uri.parse("//hostname") uri.parse("//hostname")
|> should.equal(Ok(Uri(..uri.empty_uri, host: Some("hostname")))) |> should.equal(Ok(Uri(..types.empty_uri, host: Some("hostname"))))
uri.parse("foo://hostname") uri.parse("foo://hostname")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), host: Some("hostname")), Uri(..types.empty_uri, scheme: Some("foo"), host: Some("hostname")),
)) ))
uri.parse("foo://user@hostname") uri.parse("foo://user@hostname")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
userinfo: Some("user"), userinfo: Some("user"),
host: Some("hostname"), host: Some("hostname"),
@@ -173,54 +174,58 @@ pub fn parse_host_tests() {
}), }),
it("ipv4 parse", fn() { it("ipv4 parse", fn() {
uri.parse("//127.0.0.1") uri.parse("//127.0.0.1")
|> should.equal(Ok(Uri(..uri.empty_uri, host: Some("127.0.0.1")))) |> should.equal(Ok(Uri(..types.empty_uri, host: Some("127.0.0.1"))))
uri.parse("//127.0.0.1/over/there") uri.parse("//127.0.0.1/over/there")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("127.0.0.1"), path: "/over/there"), Uri(..types.empty_uri, host: Some("127.0.0.1"), path: "/over/there"),
)) ))
uri.parse("//127.0.0.1?name=ferret") uri.parse("//127.0.0.1?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("127.0.0.1"), host: Some("127.0.0.1"),
query: Some("name=ferret"), query: Some("name=ferret"),
), ),
)) ))
uri.parse("//127.0.0.1#nose") uri.parse("//127.0.0.1#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("127.0.0.1"), fragment: Some("nose")), Uri(..types.empty_uri, host: Some("127.0.0.1"), fragment: Some("nose")),
)) ))
uri.parse("//127.0.0.x") uri.parse("//127.0.0.x")
|> should.equal(Ok(Uri(..uri.empty_uri, host: Some("127.0.0.x")))) |> should.equal(Ok(Uri(..types.empty_uri, host: Some("127.0.0.x"))))
uri.parse("//1227.0.0.1") uri.parse("//1227.0.0.1")
|> should.equal(Ok(Uri(..uri.empty_uri, host: Some("1227.0.0.1")))) |> should.equal(Ok(Uri(..types.empty_uri, host: Some("1227.0.0.1"))))
}), }),
it("ipv6 parse", fn() { it("ipv6 parse", fn() {
uri.parse("//[::127.0.0.1]") uri.parse("//[::127.0.0.1]")
|> should.equal(Ok(Uri(..uri.empty_uri, host: Some("::127.0.0.1")))) |> should.equal(Ok(Uri(..types.empty_uri, host: Some("::127.0.0.1"))))
uri.parse("//[2001:0db8:0000:0000:0000:0000:1428:07ab]") uri.parse("//[2001:0db8:0000:0000:0000:0000:1428:07ab]")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("2001:0db8:0000:0000:0000:0000:1428:07ab"), host: Some("2001:0db8:0000:0000:0000:0000:1428:07ab"),
), ),
)) ))
uri.parse("//[::127.0.0.1]/over/there") uri.parse("//[::127.0.0.1]/over/there")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("::127.0.0.1"), path: "/over/there"), Uri(..types.empty_uri, host: Some("::127.0.0.1"), path: "/over/there"),
)) ))
uri.parse("//[::127.0.0.1]?name=ferret") uri.parse("//[::127.0.0.1]?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("::127.0.0.1"), host: Some("::127.0.0.1"),
query: Some("name=ferret"), query: Some("name=ferret"),
), ),
)) ))
uri.parse("//[::127.0.0.1]#nose") uri.parse("//[::127.0.0.1]#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("::127.0.0.1"), fragment: Some("nose")), Uri(
..types.empty_uri,
host: Some("::127.0.0.1"),
fragment: Some("nose"),
),
)) ))
uri.parse("//[::127.0.0.x]") |> should.be_error uri.parse("//[::127.0.0.x]") |> should.be_error
@@ -235,21 +240,23 @@ pub fn parse_port_tests() {
describe("port parsing", [ describe("port parsing", [
it("simple parse", fn() { it("simple parse", fn() {
uri.parse("/:8042") uri.parse("/:8042")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "/:8042"))) |> should.equal(Ok(Uri(..types.empty_uri, path: "/:8042")))
uri.parse("//:8042") uri.parse("//:8042")
|> should.equal(Ok(Uri(..uri.empty_uri, host: Some(""), port: Some(8042)))) |> should.equal(Ok(
Uri(..types.empty_uri, host: Some(""), port: Some(8042)),
))
uri.parse("//example.com:8042") uri.parse("//example.com:8042")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("example.com"), port: Some(8042)), Uri(..types.empty_uri, host: Some("example.com"), port: Some(8042)),
)) ))
uri.parse("foo:/:8042") uri.parse("foo:/:8042")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), path: "/:8042"), Uri(..types.empty_uri, scheme: Some("foo"), path: "/:8042"),
)) ))
uri.parse("foo://:8042") uri.parse("foo://:8042")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some(""), host: Some(""),
port: Some(8042), port: Some(8042),
@@ -258,7 +265,7 @@ pub fn parse_port_tests() {
uri.parse("foo://example.com:8042") uri.parse("foo://example.com:8042")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
port: Some(8042), port: Some(8042),
@@ -269,23 +276,25 @@ pub fn parse_port_tests() {
}), }),
it("undefined port", fn() { it("undefined port", fn() {
uri.parse("/:") uri.parse("/:")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "/:"))) |> should.equal(Ok(Uri(..types.empty_uri, path: "/:")))
uri.parse("//:") uri.parse("//:")
|> should.equal(Ok(Uri(..uri.empty_uri, host: Some(""), port: None))) |> should.equal(Ok(Uri(..types.empty_uri, host: Some(""), port: None)))
uri.parse("//example.com:") uri.parse("//example.com:")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("example.com"), port: None), Uri(..types.empty_uri, host: Some("example.com"), port: None),
)) ))
uri.parse("foo:/:") uri.parse("foo:/:")
|> should.equal(Ok(Uri(..uri.empty_uri, scheme: Some("foo"), path: "/:"))) |> should.equal(Ok(
Uri(..types.empty_uri, scheme: Some("foo"), path: "/:"),
))
uri.parse("foo://:") uri.parse("foo://:")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), host: Some(""), port: None), Uri(..types.empty_uri, scheme: Some("foo"), host: Some(""), port: None),
)) ))
uri.parse("foo://example.com:") uri.parse("foo://example.com:")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
port: None, port: None,
@@ -301,17 +310,17 @@ pub fn parse_path_tests() {
describe("path parsing", [ describe("path parsing", [
it("simple parse", fn() { it("simple parse", fn() {
uri.parse("over/there") uri.parse("over/there")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "over/there"))) |> should.equal(Ok(Uri(..types.empty_uri, path: "over/there")))
uri.parse("/over/there") uri.parse("/over/there")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "/over/there"))) |> should.equal(Ok(Uri(..types.empty_uri, path: "/over/there")))
uri.parse("foo:/over/there") uri.parse("foo:/over/there")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), path: "/over/there"), Uri(..types.empty_uri, scheme: Some("foo"), path: "/over/there"),
)) ))
uri.parse("foo://example.com/over/there") uri.parse("foo://example.com/over/there")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
path: "/over/there", path: "/over/there",
@@ -320,7 +329,7 @@ pub fn parse_path_tests() {
uri.parse("foo://example.com:8042/over/there") uri.parse("foo://example.com:8042/over/there")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
port: Some(8042), port: Some(8042),
@@ -336,12 +345,12 @@ pub fn parse_query_tests() {
it("simple parse", fn() { it("simple parse", fn() {
uri.parse("foo:?name=ferret") uri.parse("foo:?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), query: Some("name=ferret")), Uri(..types.empty_uri, scheme: Some("foo"), query: Some("name=ferret")),
)) ))
uri.parse("foo:over/there?name=ferret") uri.parse("foo:over/there?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
path: "over/there", path: "over/there",
query: Some("name=ferret"), query: Some("name=ferret"),
@@ -350,7 +359,7 @@ pub fn parse_query_tests() {
uri.parse("foo:/over/there?name=ferret") uri.parse("foo:/over/there?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
path: "/over/there", path: "/over/there",
query: Some("name=ferret"), query: Some("name=ferret"),
@@ -359,7 +368,7 @@ pub fn parse_query_tests() {
uri.parse("foo://example.com?name=ferret") uri.parse("foo://example.com?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
query: Some("name=ferret"), query: Some("name=ferret"),
@@ -368,7 +377,7 @@ pub fn parse_query_tests() {
uri.parse("foo://example.com/?name=ferret") uri.parse("foo://example.com/?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
path: "/", path: "/",
@@ -378,24 +387,24 @@ pub fn parse_query_tests() {
uri.parse("?name=ferret") uri.parse("?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, path: "", query: Some("name=ferret")), Uri(..types.empty_uri, path: "", query: Some("name=ferret")),
)) ))
uri.parse("over/there?name=ferret") uri.parse("over/there?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, path: "over/there", query: Some("name=ferret")), Uri(..types.empty_uri, path: "over/there", query: Some("name=ferret")),
)) ))
uri.parse("/?name=ferret") uri.parse("/?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, path: "/", query: Some("name=ferret")), Uri(..types.empty_uri, path: "/", query: Some("name=ferret")),
)) ))
uri.parse("/over/there?name=ferret") uri.parse("/over/there?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, path: "/over/there", query: Some("name=ferret")), Uri(..types.empty_uri, path: "/over/there", query: Some("name=ferret")),
)) ))
uri.parse("//example.com?name=ferret") uri.parse("//example.com?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("example.com"), host: Some("example.com"),
query: Some("name=ferret"), query: Some("name=ferret"),
), ),
@@ -403,7 +412,7 @@ pub fn parse_query_tests() {
uri.parse("//example.com/?name=ferret") uri.parse("//example.com/?name=ferret")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("example.com"), host: Some("example.com"),
path: "/", path: "/",
query: Some("name=ferret"), query: Some("name=ferret"),
@@ -414,7 +423,7 @@ pub fn parse_query_tests() {
uri.parse("foo://example.com/?name=%E5%90%88%E6%B0%97%E9%81%93") uri.parse("foo://example.com/?name=%E5%90%88%E6%B0%97%E9%81%93")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
path: "/", path: "/",
@@ -424,7 +433,7 @@ pub fn parse_query_tests() {
uri.parse("//example.com/?name=%E5%90%88%E6%B0%97%E9%81%93") uri.parse("//example.com/?name=%E5%90%88%E6%B0%97%E9%81%93")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("example.com"), host: Some("example.com"),
path: "/", path: "/",
query: Some("name=%E5%90%88%E6%B0%97%E9%81%93"), query: Some("name=%E5%90%88%E6%B0%97%E9%81%93"),
@@ -439,12 +448,12 @@ pub fn parse_fragment_tests() {
it("simple parse", fn() { it("simple parse", fn() {
uri.parse("foo:#nose") uri.parse("foo:#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("foo"), fragment: Some("nose")), Uri(..types.empty_uri, scheme: Some("foo"), fragment: Some("nose")),
)) ))
uri.parse("foo:over/there#nose") uri.parse("foo:over/there#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
path: "over/there", path: "over/there",
fragment: Some("nose"), fragment: Some("nose"),
@@ -453,7 +462,7 @@ pub fn parse_fragment_tests() {
uri.parse("foo:/over/there#nose") uri.parse("foo:/over/there#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
path: "/over/there", path: "/over/there",
fragment: Some("nose"), fragment: Some("nose"),
@@ -462,7 +471,7 @@ pub fn parse_fragment_tests() {
uri.parse("foo://example.com#nose") uri.parse("foo://example.com#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
fragment: Some("nose"), fragment: Some("nose"),
@@ -471,7 +480,7 @@ pub fn parse_fragment_tests() {
uri.parse("foo://example.com/#nose") uri.parse("foo://example.com/#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
path: "/", path: "/",
@@ -481,7 +490,7 @@ pub fn parse_fragment_tests() {
uri.parse("foo://example.com#nose") uri.parse("foo://example.com#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
fragment: Some("nose"), fragment: Some("nose"),
@@ -489,27 +498,31 @@ pub fn parse_fragment_tests() {
)) ))
uri.parse("#nose") uri.parse("#nose")
|> should.equal(Ok(Uri(..uri.empty_uri, fragment: Some("nose")))) |> should.equal(Ok(Uri(..types.empty_uri, fragment: Some("nose"))))
uri.parse("over/there#nose") uri.parse("over/there#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, path: "over/there", fragment: Some("nose")), Uri(..types.empty_uri, path: "over/there", fragment: Some("nose")),
)) ))
uri.parse("/#nose") uri.parse("/#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, path: "/", fragment: Some("nose")), Uri(..types.empty_uri, path: "/", fragment: Some("nose")),
)) ))
uri.parse("/over/there#nose") uri.parse("/over/there#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, path: "/over/there", fragment: Some("nose")), Uri(..types.empty_uri, path: "/over/there", fragment: Some("nose")),
)) ))
uri.parse("//example.com#nose") uri.parse("//example.com#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("example.com"), fragment: Some("nose")), Uri(
..types.empty_uri,
host: Some("example.com"),
fragment: Some("nose"),
),
)) ))
uri.parse("//example.com/#nose") uri.parse("//example.com/#nose")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("example.com"), host: Some("example.com"),
path: "/", path: "/",
fragment: Some("nose"), fragment: Some("nose"),
@@ -520,7 +533,7 @@ pub fn parse_fragment_tests() {
uri.parse("foo://example.com#%E5%90%88%E6%B0%97%E9%81%93") uri.parse("foo://example.com#%E5%90%88%E6%B0%97%E9%81%93")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some("example.com"), host: Some("example.com"),
fragment: Some("%E5%90%88%E6%B0%97%E9%81%93"), fragment: Some("%E5%90%88%E6%B0%97%E9%81%93"),
@@ -529,7 +542,7 @@ pub fn parse_fragment_tests() {
uri.parse("//example.com/#%E5%90%88%E6%B0%97%E9%81%93") uri.parse("//example.com/#%E5%90%88%E6%B0%97%E9%81%93")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("example.com"), host: Some("example.com"),
path: "/", path: "/",
fragment: Some("%E5%90%88%E6%B0%97%E9%81%93"), fragment: Some("%E5%90%88%E6%B0%97%E9%81%93"),
@@ -543,15 +556,17 @@ fn parse_special_tests() {
describe("special parsing", [ describe("special parsing", [
it("special 1", fn() { it("special 1", fn() {
uri.parse("//?") uri.parse("//?")
|> should.equal(Ok(Uri(..uri.empty_uri, host: Some(""), query: Some("")))) |> should.equal(Ok(
Uri(..types.empty_uri, host: Some(""), query: Some("")),
))
uri.parse("//#") uri.parse("//#")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some(""), fragment: Some("")), Uri(..types.empty_uri, host: Some(""), fragment: Some("")),
)) ))
uri.parse("//?#") uri.parse("//?#")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some(""), host: Some(""),
query: Some(""), query: Some(""),
fragment: Some(""), fragment: Some(""),
@@ -560,7 +575,7 @@ fn parse_special_tests() {
uri.parse("foo://?") uri.parse("foo://?")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some(""), host: Some(""),
query: Some(""), query: Some(""),
@@ -569,7 +584,7 @@ fn parse_special_tests() {
uri.parse("foo://#") uri.parse("foo://#")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some(""), host: Some(""),
fragment: Some(""), fragment: Some(""),
@@ -578,7 +593,7 @@ fn parse_special_tests() {
uri.parse("foo://?#") uri.parse("foo://?#")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some(""), host: Some(""),
query: Some(""), query: Some(""),
@@ -586,23 +601,23 @@ fn parse_special_tests() {
), ),
)) ))
uri.parse("///") uri.parse("///")
|> should.equal(Ok(Uri(..uri.empty_uri, host: Some(""), path: "/"))) |> should.equal(Ok(Uri(..types.empty_uri, host: Some(""), path: "/")))
uri.parse("///hostname") uri.parse("///hostname")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some(""), path: "/hostname"), Uri(..types.empty_uri, host: Some(""), path: "/hostname"),
)) ))
uri.parse("///?") uri.parse("///?")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some(""), path: "/", query: Some("")), Uri(..types.empty_uri, host: Some(""), path: "/", query: Some("")),
)) ))
uri.parse("///#") uri.parse("///#")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some(""), path: "/", fragment: Some("")), Uri(..types.empty_uri, host: Some(""), path: "/", fragment: Some("")),
)) ))
uri.parse("///?#") uri.parse("///?#")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some(""), host: Some(""),
path: "/", path: "/",
query: Some(""), query: Some(""),
@@ -611,27 +626,27 @@ fn parse_special_tests() {
)) ))
uri.parse("//foo?") uri.parse("//foo?")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("foo"), query: Some("")), Uri(..types.empty_uri, host: Some("foo"), query: Some("")),
)) ))
uri.parse("//foo#") uri.parse("//foo#")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("foo"), fragment: Some("")), Uri(..types.empty_uri, host: Some("foo"), fragment: Some("")),
)) ))
uri.parse("//foo?#") uri.parse("//foo?#")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
host: Some("foo"), host: Some("foo"),
query: Some(""), query: Some(""),
fragment: Some(""), fragment: Some(""),
), ),
)) ))
uri.parse("//foo/") uri.parse("//foo/")
|> should.equal(Ok(Uri(..uri.empty_uri, host: Some("foo"), path: "/"))) |> should.equal(Ok(Uri(..types.empty_uri, host: Some("foo"), path: "/")))
uri.parse("http://foo?") uri.parse("http://foo?")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("http"), scheme: Some("http"),
host: Some("foo"), host: Some("foo"),
query: Some(""), query: Some(""),
@@ -640,7 +655,7 @@ fn parse_special_tests() {
uri.parse("http://foo#") uri.parse("http://foo#")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("http"), scheme: Some("http"),
host: Some("foo"), host: Some("foo"),
fragment: Some(""), fragment: Some(""),
@@ -649,7 +664,7 @@ fn parse_special_tests() {
uri.parse("http://foo?#") uri.parse("http://foo?#")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("http"), scheme: Some("http"),
host: Some("foo"), host: Some("foo"),
query: Some(""), query: Some(""),
@@ -658,12 +673,17 @@ fn parse_special_tests() {
)) ))
uri.parse("http://foo/") uri.parse("http://foo/")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, scheme: Some("http"), host: Some("foo"), path: "/"), Uri(
..types.empty_uri,
scheme: Some("http"),
host: Some("foo"),
path: "/",
),
)) ))
uri.parse("http://foo:80?") uri.parse("http://foo:80?")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("http"), scheme: Some("http"),
host: Some("foo"), host: Some("foo"),
port: Some(80), port: Some(80),
@@ -673,7 +693,7 @@ fn parse_special_tests() {
uri.parse("http://foo:80#") uri.parse("http://foo:80#")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("http"), scheme: Some("http"),
host: Some("foo"), host: Some("foo"),
port: Some(80), port: Some(80),
@@ -683,7 +703,7 @@ fn parse_special_tests() {
uri.parse("http://foo:80?#") uri.parse("http://foo:80?#")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("http"), scheme: Some("http"),
host: Some("foo"), host: Some("foo"),
port: Some(80), port: Some(80),
@@ -694,7 +714,7 @@ fn parse_special_tests() {
uri.parse("http://foo:80/") uri.parse("http://foo:80/")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("http"), scheme: Some("http"),
host: Some("foo"), host: Some("foo"),
port: Some(80), port: Some(80),
@@ -702,23 +722,23 @@ fn parse_special_tests() {
), ),
)) ))
uri.parse("?") uri.parse("?")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "", query: Some("")))) |> should.equal(Ok(Uri(..types.empty_uri, path: "", query: Some(""))))
uri.parse("??") uri.parse("??")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "", query: Some("?")))) |> should.equal(Ok(Uri(..types.empty_uri, path: "", query: Some("?"))))
uri.parse("???") uri.parse("???")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "", query: Some("??")))) |> should.equal(Ok(Uri(..types.empty_uri, path: "", query: Some("??"))))
uri.parse("#") uri.parse("#")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "", fragment: Some("")))) |> should.equal(Ok(Uri(..types.empty_uri, path: "", fragment: Some(""))))
uri.parse("##") uri.parse("##")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "", fragment: Some("#")))) |> should.equal(Ok(Uri(..types.empty_uri, path: "", fragment: Some("#"))))
uri.parse("###") uri.parse("###")
|> should.equal(Ok(Uri(..uri.empty_uri, path: "", fragment: Some("##")))) |> should.equal(Ok(Uri(..types.empty_uri, path: "", fragment: Some("##"))))
}), }),
it("special 2", fn() { it("special 2", fn() {
uri.parse("a://:1/") uri.parse("a://:1/")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("a"), scheme: Some("a"),
host: Some(""), host: Some(""),
port: Some(1), port: Some(1),
@@ -726,15 +746,15 @@ fn parse_special_tests() {
), ),
)) ))
uri.parse("a:/a/") uri.parse("a:/a/")
|> should.equal(Ok(Uri(..uri.empty_uri, scheme: Some("a"), path: "/a/"))) |> should.equal(Ok(Uri(..types.empty_uri, scheme: Some("a"), path: "/a/")))
uri.parse("//@") uri.parse("//@")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some(""), path: "", userinfo: Some("")), Uri(..types.empty_uri, host: Some(""), path: "", userinfo: Some("")),
)) ))
uri.parse("foo://@") uri.parse("foo://@")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some(""), host: Some(""),
path: "", path: "",
@@ -743,12 +763,12 @@ fn parse_special_tests() {
)) ))
uri.parse("//@/") uri.parse("//@/")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some(""), path: "/", userinfo: Some("")), Uri(..types.empty_uri, host: Some(""), path: "/", userinfo: Some("")),
)) ))
uri.parse("foo://@/") uri.parse("foo://@/")
|> should.equal(Ok( |> should.equal(Ok(
Uri( Uri(
..uri.empty_uri, ..types.empty_uri,
scheme: Some("foo"), scheme: Some("foo"),
host: Some(""), host: Some(""),
path: "/", path: "/",
@@ -757,11 +777,11 @@ fn parse_special_tests() {
)) ))
uri.parse("//localhost:/") uri.parse("//localhost:/")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("localhost"), path: "/"), Uri(..types.empty_uri, host: Some("localhost"), path: "/"),
)) ))
uri.parse("//:") uri.parse("//:")
|> should.equal(Ok( |> should.equal(Ok(
Uri(..uri.empty_uri, host: Some("localhost"), path: ""), Uri(..types.empty_uri, host: Some("localhost"), path: ""),
)) ))
}), }),
]) ])
@@ -777,6 +797,8 @@ pub fn parse_failure_tests() {
uri.parse("//host/path#foö") |> should.be_error uri.parse("//host/path#foö") |> should.be_error
uri.parse("//[:::127.0.0.1]") |> should.be_error uri.parse("//[:::127.0.0.1]") |> should.be_error
uri.parse("//localhost:A8") |> should.be_error uri.parse("//localhost:A8") |> should.be_error
uri.parse("http://f%ff%%ff/") |> should.be_error
uri.parse("http://f%ff%fr/") |> should.be_error
}), }),
]) ])
} }