perf: Optimisations for parsing between options
Added a specific parse try function which takes the essence of list.fold_until but makes it specific for fn(String)->Result(#(a, String),Nil) parsers ??? ??
This commit is contained in:
		@@ -7,3 +7,7 @@
 | 
				
			|||||||
## v2.0.0
 | 
					## v2.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Removed types.Uri. Now gluri uses the stdlib Uri type (and empty)
 | 
					- Removed types.Uri. Now gluri uses the stdlib Uri type (and empty)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## v2.0.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Improved parsing performance slightly and reduced memory usage up to 50%
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ import gleam/int
 | 
				
			|||||||
import gleam/list
 | 
					import gleam/list
 | 
				
			||||||
import gleam/option.{Some}
 | 
					import gleam/option.{Some}
 | 
				
			||||||
import gleam/string
 | 
					import gleam/string
 | 
				
			||||||
import gleam/uri.{type Uri, Uri}
 | 
					import gleam/uri.{type Uri}
 | 
				
			||||||
import gluri/internal/parser
 | 
					import gluri/internal/parser
 | 
				
			||||||
import gluri/internal/utils
 | 
					import gluri/internal/utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,8 @@ pub fn parse(uri: String) -> Result(Uri, Nil) {
 | 
				
			|||||||
fn parse_query(str: String) -> Result(#(Uri, String), Nil) {
 | 
					fn parse_query(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			||||||
  case str {
 | 
					  case str {
 | 
				
			||||||
    "?" <> rest -> {
 | 
					    "?" <> rest -> {
 | 
				
			||||||
      let #(query, rest) = get_multiple_optional(parse_query_fragment, rest)
 | 
					      let #(query, rest) =
 | 
				
			||||||
 | 
					        utils.get_multiple_optional(parse_query_fragment, rest)
 | 
				
			||||||
      Ok(#(Uri(..empty, query: Some(query)), rest))
 | 
					      Ok(#(Uri(..empty, query: Some(query)), rest))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    _ -> Ok(#(empty, str))
 | 
					    _ -> Ok(#(empty, str))
 | 
				
			||||||
@@ -50,7 +51,8 @@ fn parse_query(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			|||||||
fn parse_fragment(str: String) -> Result(#(Uri, String), Nil) {
 | 
					fn parse_fragment(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			||||||
  case str {
 | 
					  case str {
 | 
				
			||||||
    "#" <> rest -> {
 | 
					    "#" <> rest -> {
 | 
				
			||||||
      let #(fragment, rest) = get_multiple_optional(parse_query_fragment, rest)
 | 
					      let #(fragment, rest) =
 | 
				
			||||||
 | 
					        utils.get_multiple_optional(parse_query_fragment, rest)
 | 
				
			||||||
      Ok(#(Uri(..empty, fragment: Some(fragment)), rest))
 | 
					      Ok(#(Uri(..empty, fragment: Some(fragment)), rest))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    _ -> Ok(#(empty, str))
 | 
					    _ -> Ok(#(empty, str))
 | 
				
			||||||
@@ -58,29 +60,27 @@ fn parse_fragment(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_hier_part(str: String) -> Result(#(Uri, String), Nil) {
 | 
					fn parse_hier_part(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			||||||
  list.fold_until(
 | 
					  utils.try_parsers(
 | 
				
			||||||
    [parse_authority, parse_absolute, parse_rootless, parse_empty],
 | 
					    [parse_authority, parse_absolute, parse_rootless, parse_empty],
 | 
				
			||||||
    Error(Nil),
 | 
					    str,
 | 
				
			||||||
    get_parser_fn(str),
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_relative_part(str: String) -> Result(#(Uri, String), Nil) {
 | 
					fn parse_relative_part(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			||||||
  list.fold_until(
 | 
					  utils.try_parsers(
 | 
				
			||||||
    [parse_authority, parse_absolute, parse_noscheme, parse_empty],
 | 
					    [parse_authority, parse_absolute, parse_noscheme, parse_empty],
 | 
				
			||||||
    Error(Nil),
 | 
					    str,
 | 
				
			||||||
    get_parser_fn(str),
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_absolute(str: String) -> Result(#(Uri, String), Nil) {
 | 
					fn parse_absolute(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			||||||
  case str {
 | 
					  case str {
 | 
				
			||||||
    "/" <> rest -> {
 | 
					    "/" <> rest -> {
 | 
				
			||||||
      let assert Ok(#(seg, rest)) =
 | 
					      use #(seg, rest) <- result.try(
 | 
				
			||||||
        parse_optional(rest, parse_this_then(
 | 
					        parse_optional(rest, parse_this_then(
 | 
				
			||||||
          [
 | 
					          [
 | 
				
			||||||
            do_parse_segment_nz,
 | 
					            do_parse_segment_nz,
 | 
				
			||||||
            get_multiple_optional_result(
 | 
					            utils.get_multiple_optional_result(
 | 
				
			||||||
              fn(str) {
 | 
					              fn(str) {
 | 
				
			||||||
                case str {
 | 
					                case str {
 | 
				
			||||||
                  "/" <> rest -> {
 | 
					                  "/" <> rest -> {
 | 
				
			||||||
@@ -93,7 +93,8 @@ fn parse_absolute(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			|||||||
            ),
 | 
					            ),
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
          _,
 | 
					          _,
 | 
				
			||||||
        ))
 | 
					        )),
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      Ok(#(Uri(None, None, None, None, "/" <> seg, None, None), rest))
 | 
					      Ok(#(Uri(None, None, None, None, "/" <> seg, None, None), rest))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -105,7 +106,7 @@ fn parse_rootless(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			|||||||
  use #(seg1, rest) <- result.try(do_parse_segment_nz(str))
 | 
					  use #(seg1, rest) <- result.try(do_parse_segment_nz(str))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let #(segs, rest) =
 | 
					  let #(segs, rest) =
 | 
				
			||||||
    get_multiple_optional(
 | 
					    utils.get_multiple_optional(
 | 
				
			||||||
      fn(str) {
 | 
					      fn(str) {
 | 
				
			||||||
        case str {
 | 
					        case str {
 | 
				
			||||||
          "/" <> rest -> {
 | 
					          "/" <> rest -> {
 | 
				
			||||||
@@ -124,7 +125,7 @@ fn parse_noscheme(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			|||||||
  use #(seg1, rest) <- result.try(do_parse_segment_nz_nc(str))
 | 
					  use #(seg1, rest) <- result.try(do_parse_segment_nz_nc(str))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let #(segs, rest) =
 | 
					  let #(segs, rest) =
 | 
				
			||||||
    get_multiple_optional(
 | 
					    utils.get_multiple_optional(
 | 
				
			||||||
      fn(str) {
 | 
					      fn(str) {
 | 
				
			||||||
        case str {
 | 
					        case str {
 | 
				
			||||||
          "/" <> rest -> {
 | 
					          "/" <> rest -> {
 | 
				
			||||||
@@ -146,17 +147,6 @@ fn parse_optional(str, opt_fn) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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) {
 | 
					fn parse_empty(str: String) -> Result(#(Uri, String), Nil) {
 | 
				
			||||||
  Ok(#(Uri(None, None, None, None, "", None, None), str))
 | 
					  Ok(#(Uri(None, None, None, None, "", None, None), str))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -211,20 +201,15 @@ fn parse_digits(str: String, digits: String) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_host(str: String) {
 | 
					fn parse_host(str: String) {
 | 
				
			||||||
  list.fold_until(
 | 
					  utils.try_parsers([parse_ip_literal, parse_ipv4, parse_reg_name], str)
 | 
				
			||||||
    [parse_ip_literal, parse_ipv4, parse_reg_name],
 | 
					 | 
				
			||||||
    Error(Nil),
 | 
					 | 
				
			||||||
    get_parser_fn(str),
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_ip_literal(str: String) {
 | 
					fn parse_ip_literal(str: String) {
 | 
				
			||||||
  case str {
 | 
					  case str {
 | 
				
			||||||
    "[" <> rest -> {
 | 
					    "[" <> rest -> {
 | 
				
			||||||
      use #(ip, rest) <- result.try(list.fold_until(
 | 
					      use #(ip, rest) <- result.try(utils.try_parsers(
 | 
				
			||||||
        [parse_ipv6, parse_ipfuture],
 | 
					        [parse_ipv6, parse_ipfuture],
 | 
				
			||||||
        Error(Nil),
 | 
					        rest,
 | 
				
			||||||
        get_parser_fn(rest),
 | 
					 | 
				
			||||||
      ))
 | 
					      ))
 | 
				
			||||||
      case rest {
 | 
					      case rest {
 | 
				
			||||||
        "]" <> rest -> Ok(#(ip, rest))
 | 
					        "]" <> rest -> Ok(#(ip, rest))
 | 
				
			||||||
@@ -236,7 +221,7 @@ fn parse_ip_literal(str: String) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_ipv6(str: String) {
 | 
					fn parse_ipv6(str: String) {
 | 
				
			||||||
  list.fold_until(
 | 
					  utils.try_parsers(
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
      parse_this_then([parse_min_max(_, 6, 6, parse_h16_colon), parse_ls32], _),
 | 
					      parse_this_then([parse_min_max(_, 6, 6, parse_h16_colon), parse_ls32], _),
 | 
				
			||||||
      parse_this_then(
 | 
					      parse_this_then(
 | 
				
			||||||
@@ -303,8 +288,7 @@ fn parse_ipv6(str: String) {
 | 
				
			|||||||
        _,
 | 
					        _,
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    Error(Nil),
 | 
					    str,
 | 
				
			||||||
    get_parser_fn(str),
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -335,7 +319,7 @@ fn parse_this_then(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_ls32(str: String) -> Result(#(String, String), Nil) {
 | 
					fn parse_ls32(str: String) -> Result(#(String, String), Nil) {
 | 
				
			||||||
  list.fold_until([parse_h16_pair, parse_ipv4], Error(Nil), get_parser_fn(str))
 | 
					  utils.try_parsers([parse_h16_pair, parse_ipv4], str)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_h16_pair(str: String) {
 | 
					fn parse_h16_pair(str: String) {
 | 
				
			||||||
@@ -364,13 +348,16 @@ fn parse_h16_colon(str: String) {
 | 
				
			|||||||
fn parse_ipfuture(str: String) {
 | 
					fn parse_ipfuture(str: String) {
 | 
				
			||||||
  case str {
 | 
					  case str {
 | 
				
			||||||
    "v" <> rest -> {
 | 
					    "v" <> rest -> {
 | 
				
			||||||
      use #(v, rest) <- result.try(get_multiple(utils.parse_hex_digit, rest))
 | 
					      use #(v, rest) <- result.try(utils.get_multiple(
 | 
				
			||||||
 | 
					        utils.parse_hex_digit,
 | 
				
			||||||
 | 
					        rest,
 | 
				
			||||||
 | 
					      ))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case rest {
 | 
					      case rest {
 | 
				
			||||||
        "." <> rest -> {
 | 
					        "." <> rest -> {
 | 
				
			||||||
          use #(i, rest) <- result.try(get_multiple(
 | 
					          use #(i, rest) <- result.try(utils.get_multiple(
 | 
				
			||||||
            fn(str) {
 | 
					            fn(str) {
 | 
				
			||||||
              list.fold_until(
 | 
					              utils.try_parsers(
 | 
				
			||||||
                [
 | 
					                [
 | 
				
			||||||
                  parse_unreserved,
 | 
					                  parse_unreserved,
 | 
				
			||||||
                  parse_sub_delim,
 | 
					                  parse_sub_delim,
 | 
				
			||||||
@@ -381,8 +368,7 @@ fn parse_ipfuture(str: String) {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                  },
 | 
					                  },
 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
                Error(Nil),
 | 
					                str,
 | 
				
			||||||
                get_parser_fn(str),
 | 
					 | 
				
			||||||
              )
 | 
					              )
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            rest,
 | 
					            rest,
 | 
				
			||||||
@@ -396,33 +382,8 @@ fn parse_ipfuture(str: String) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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) {
 | 
					fn parse_query_fragment(str: String) {
 | 
				
			||||||
  list.fold_until(
 | 
					  utils.try_parsers(
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
      do_parse_pchar,
 | 
					      do_parse_pchar,
 | 
				
			||||||
      fn(str: String) {
 | 
					      fn(str: String) {
 | 
				
			||||||
@@ -432,13 +393,12 @@ fn parse_query_fragment(str: String) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    Error(Nil),
 | 
					    str,
 | 
				
			||||||
    get_parser_fn(str),
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_abs_empty(str: String) -> #(String, String) {
 | 
					fn parse_abs_empty(str: String) -> #(String, String) {
 | 
				
			||||||
  get_multiple_optional(
 | 
					  utils.get_multiple_optional(
 | 
				
			||||||
    fn(str) {
 | 
					    fn(str) {
 | 
				
			||||||
      case str {
 | 
					      case str {
 | 
				
			||||||
        "/" <> rest -> {
 | 
					        "/" <> rest -> {
 | 
				
			||||||
@@ -483,7 +443,7 @@ fn do_parse_segment_nz_nc(str: String) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn do_parse_pchar(str: String) {
 | 
					fn do_parse_pchar(str: String) {
 | 
				
			||||||
  list.fold_until(
 | 
					  utils.try_parsers(
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
      parse_unreserved,
 | 
					      parse_unreserved,
 | 
				
			||||||
      parse_pct_encoded,
 | 
					      parse_pct_encoded,
 | 
				
			||||||
@@ -495,13 +455,12 @@ fn do_parse_pchar(str: String) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    Error(Nil),
 | 
					    str,
 | 
				
			||||||
    get_parser_fn(str),
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn do_parse_pchar_nc(str: String) {
 | 
					fn do_parse_pchar_nc(str: String) {
 | 
				
			||||||
  list.fold_until(
 | 
					  utils.try_parsers(
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
      parse_unreserved,
 | 
					      parse_unreserved,
 | 
				
			||||||
      parse_pct_encoded,
 | 
					      parse_pct_encoded,
 | 
				
			||||||
@@ -513,12 +472,11 @@ fn do_parse_pchar_nc(str: String) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    Error(Nil),
 | 
					    str,
 | 
				
			||||||
    get_parser_fn(str),
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_reg_name(str: String) {
 | 
					pub fn parse_reg_name(str: String) {
 | 
				
			||||||
  // can't error
 | 
					  // can't error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  case do_parse_reg_name(str, "") {
 | 
					  case do_parse_reg_name(str, "") {
 | 
				
			||||||
@@ -529,10 +487,9 @@ fn parse_reg_name(str: String) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
fn do_parse_reg_name(str: String, reg_name: String) {
 | 
					fn do_parse_reg_name(str: String, reg_name: String) {
 | 
				
			||||||
  case
 | 
					  case
 | 
				
			||||||
    list.fold_until(
 | 
					    utils.try_parsers(
 | 
				
			||||||
      [parse_unreserved, parse_pct_encoded, parse_sub_delim],
 | 
					      [parse_unreserved, parse_pct_encoded, parse_sub_delim],
 | 
				
			||||||
      Error(Nil),
 | 
					      str,
 | 
				
			||||||
      get_parser_fn(str),
 | 
					 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    Error(Nil) | Ok(#("", _)) -> Ok(#(reg_name, str))
 | 
					    Error(Nil) | Ok(#("", _)) -> Ok(#(reg_name, str))
 | 
				
			||||||
@@ -589,16 +546,16 @@ fn parse_ipv4(str: String) {
 | 
				
			|||||||
  Ok(#(oct1 <> "." <> oct2 <> "." <> oct3 <> "." <> oct4, rest))
 | 
					  Ok(#(oct1 <> "." <> oct2 <> "." <> oct3 <> "." <> oct4, rest))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_dec_octet(str: String) -> Result(#(String, String), Nil) {
 | 
					const octet_matches = [
 | 
				
			||||||
  let matches = [
 | 
					 | 
				
			||||||
  ["2", "5", "012345"],
 | 
					  ["2", "5", "012345"],
 | 
				
			||||||
  ["2", "01234", "0123456789"],
 | 
					  ["2", "01234", "0123456789"],
 | 
				
			||||||
  ["1", "0123456789", "0123456789"],
 | 
					  ["1", "0123456789", "0123456789"],
 | 
				
			||||||
  ["123456789", "0123456789"],
 | 
					  ["123456789", "0123456789"],
 | 
				
			||||||
  ["0123456789"],
 | 
					  ["0123456789"],
 | 
				
			||||||
  ]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  list.fold_until(matches, Error(Nil), fn(_, chars) {
 | 
					fn parse_dec_octet(str: String) -> Result(#(String, String), Nil) {
 | 
				
			||||||
 | 
					  list.fold_until(octet_matches, Error(Nil), fn(_, chars) {
 | 
				
			||||||
    case
 | 
					    case
 | 
				
			||||||
      list.fold_until(chars, #("", str), fn(acc, charset) {
 | 
					      list.fold_until(chars, #("", str), fn(acc, charset) {
 | 
				
			||||||
        let #(octet, str) = acc
 | 
					        let #(octet, str) = acc
 | 
				
			||||||
@@ -627,7 +584,7 @@ fn parse_userinfo(
 | 
				
			|||||||
    "@" <> rest -> Ok(#(userinfo, rest))
 | 
					    "@" <> rest -> Ok(#(userinfo, rest))
 | 
				
			||||||
    "" -> Error(Nil)
 | 
					    "" -> Error(Nil)
 | 
				
			||||||
    _ -> {
 | 
					    _ -> {
 | 
				
			||||||
      use #(part, rest) <- result.try(list.fold_until(
 | 
					      use #(part, rest) <- result.try(utils.try_parsers(
 | 
				
			||||||
        [
 | 
					        [
 | 
				
			||||||
          parse_unreserved,
 | 
					          parse_unreserved,
 | 
				
			||||||
          parse_pct_encoded,
 | 
					          parse_pct_encoded,
 | 
				
			||||||
@@ -639,8 +596,7 @@ fn parse_userinfo(
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        Error(Nil),
 | 
					        str,
 | 
				
			||||||
        get_parser_fn(str),
 | 
					 | 
				
			||||||
      ))
 | 
					      ))
 | 
				
			||||||
      parse_userinfo(rest, userinfo <> part)
 | 
					      parse_userinfo(rest, userinfo <> part)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -668,7 +624,7 @@ fn do_parse_scheme(
 | 
				
			|||||||
    ":" <> rest -> Ok(#(scheme, rest))
 | 
					    ":" <> rest -> Ok(#(scheme, rest))
 | 
				
			||||||
    "" -> Error(Nil)
 | 
					    "" -> Error(Nil)
 | 
				
			||||||
    _ -> {
 | 
					    _ -> {
 | 
				
			||||||
      use #(part, rest) <- result.try(list.fold_until(
 | 
					      use #(part, rest) <- result.try(utils.try_parsers(
 | 
				
			||||||
        [
 | 
					        [
 | 
				
			||||||
          parse_alpha,
 | 
					          parse_alpha,
 | 
				
			||||||
          parse_digit,
 | 
					          parse_digit,
 | 
				
			||||||
@@ -680,25 +636,13 @@ fn do_parse_scheme(
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        Error(Nil),
 | 
					        str,
 | 
				
			||||||
        get_parser_fn(str),
 | 
					 | 
				
			||||||
      ))
 | 
					      ))
 | 
				
			||||||
      do_parse_scheme(rest, scheme <> part)
 | 
					      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) {
 | 
					fn parse_min_max(str, min, max, parse_fn) {
 | 
				
			||||||
  use <- bool.guard(when: min < 0 || max <= 0 || min > max, return: Error(Nil))
 | 
					  use <- bool.guard(when: min < 0 || max <= 0 || min > max, return: Error(Nil))
 | 
				
			||||||
  case
 | 
					  case
 | 
				
			||||||
@@ -799,7 +743,7 @@ fn parse_alpha(str: String) -> Result(#(String, String), Nil) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn parse_unreserved(str: String) -> Result(#(String, String), Nil) {
 | 
					fn parse_unreserved(str: String) -> Result(#(String, String), Nil) {
 | 
				
			||||||
  list.fold_until(
 | 
					  utils.try_parsers(
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
      parse_alpha,
 | 
					      parse_alpha,
 | 
				
			||||||
      parse_digit,
 | 
					      parse_digit,
 | 
				
			||||||
@@ -813,8 +757,7 @@ fn parse_unreserved(str: String) -> Result(#(String, String), Nil) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    Error(Nil),
 | 
					    str,
 | 
				
			||||||
    get_parser_fn(str),
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import gleam/bool
 | 
					import gleam/bool
 | 
				
			||||||
import gleam/int
 | 
					import gleam/int
 | 
				
			||||||
import gleam/list
 | 
					import gleam/list
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import gleam/option.{type Option, None, Some}
 | 
					import gleam/option.{type Option, None, Some}
 | 
				
			||||||
import gleam/result
 | 
					import gleam/result
 | 
				
			||||||
import gleam/string
 | 
					import gleam/string
 | 
				
			||||||
@@ -86,6 +87,56 @@ fn merge_paths(base: Uri, relative: Uri) -> String {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn try_parsers(
 | 
				
			||||||
 | 
					  over list: List(fn(String) -> Result(#(a, String), Nil)),
 | 
				
			||||||
 | 
					  against static_data: String,
 | 
				
			||||||
 | 
					) -> Result(#(a, String), Nil) {
 | 
				
			||||||
 | 
					  case list {
 | 
				
			||||||
 | 
					    [] -> Error(Nil)
 | 
				
			||||||
 | 
					    [first, ..rest] ->
 | 
				
			||||||
 | 
					      case first(static_data) {
 | 
				
			||||||
 | 
					        Error(_) -> try_parsers(rest, static_data)
 | 
				
			||||||
 | 
					        Ok(r) -> Ok(r)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub 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))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn get_multiple_optional(opt_fn, str: String) {
 | 
				
			||||||
 | 
					  case get_multiple(opt_fn, str) {
 | 
				
			||||||
 | 
					    Error(_) -> #("", str)
 | 
				
			||||||
 | 
					    Ok(r) -> r
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn get_multiple_optional_result(opt_fn, str: String) {
 | 
				
			||||||
 | 
					  get_multiple_optional(opt_fn, str) |> Ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn normalise(uri: Uri) -> Uri {
 | 
					pub fn normalise(uri: Uri) -> Uri {
 | 
				
			||||||
  let percent_splitter = splitter.new(["%"])
 | 
					  let percent_splitter = splitter.new(["%"])
 | 
				
			||||||
  let percent_normaliser = normalise_percent(percent_splitter, _)
 | 
					  let percent_normaliser = normalise_percent(percent_splitter, _)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user