feat: Refactor and added normali(s/z)e
This commit is contained in:
		@@ -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"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
									
								
							
							
						
						
									
										850
									
								
								src/internal/parser.gleam
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										15
									
								
								src/types.gleam
									
									
									
									
									
										Normal 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)
 | 
				
			||||||
							
								
								
									
										997
									
								
								src/uri.gleam
									
									
									
									
									
								
							
							
						
						
									
										997
									
								
								src/uri.gleam
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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
 | 
				
			||||||
    }),
 | 
					    }),
 | 
				
			||||||
  ])
 | 
					  ])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user