~/bend-docscommunity

lib.bend source

lib.bend on the hub · documented module

# Url — a small Base-only URL parser and builder for Bend 2.0.2.## Url v1 models four textual fields: scheme, host, path, and query. Parsing is# deliberately narrower than RFC 3986: only absolute http:// and https:// URLs# are accepted; the authority must contain a non-empty host and no userinfo.# Paths and queries are retained as text, without percent decoding.import Basetype Url is Data:  Url{scheme: String, host: String, path: String, query: String}# Construct a URL value without validation. Use Url.parse for the v1 grammar.def Url.make(scheme: String, host: String, path: String, query: String) -> Url:  Url{scheme, host, path, query}# Parse a slash-delimited path tail.def Url.parse.path(  scheme: String,  host: String,  query: String,  +tail: List<&2, String>) -> Maybe<&2, Url>:  match tail:    case Nil{}:      Some{Url{scheme, host, "", query}}    case +first <> +rest:      Some{Url{        scheme,        host,        String.append("/", String.join(first <> rest, "/")),        query      }}# Reject empty hosts and the userinfo marker before building a URL.def Url.parse.authority.host(  scheme: String,  query: String,  host: String,  +tail: List<&2, String>) -> Maybe<&2, Url>:  match host:    case SNil{}:      None{}    case SCon{+h, +t}:      Bool.pick(        Maybe<&2, Url>,        String.contains(SCon{h, t}, "@"),        None{},        Url.parse.path(scheme, SCon{h, t}, query, tail)      )# Parse the authority and path after query splitting. The first slash-delimited# component is the host; the remaining components are rejoined verbatim.def Url.parse.authority(  scheme: String,  query: String,  +parts: List<&2, String>) -> Maybe<&2, Url>:  match parts:    case Nil{}:      None{}    case +host <> +tail:      Url.parse.authority.host(scheme, query, host, tail)# A query is optional but, when present, must occur exactly once. Fragment# syntax is outside this subset and is rejected by parse.scheme.def Url.parse.query(  scheme: String,  +parts: List<&2, String>) -> Maybe<&2, Url>:  match parts:    case Nil{}:      None{}    case +before <> Nil{}:      Url.parse.authority(scheme, "", String.split(before, '/'))    case +before <> +query <> Nil{}:      Url.parse.authority(scheme, query, String.split(before, '/'))    case _ <> _ <> _:      None{}def Url.parse.after_scheme(  scheme: String,  +rest: String) -> Maybe<&2, Url>:  Bool.pick(    Maybe<&2, Url>,    String.contains(rest, "#"),    None{},    Url.parse.query(scheme, String.split(rest, '?'))  )# Match the fixed lowercase "http://" or "https://" prefix without# duplicating the input string in a Boolean branch.def Url.parse.https.t8(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 47),        Url.parse.after_scheme("https", t), None{})def Url.parse.https.t7(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 47),        Url.parse.https.t8(t), None{})def Url.parse.https.t6(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 58),        Url.parse.https.t7(t), None{})def Url.parse.https.t5(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 115),        Url.parse.https.t6(t), None{})def Url.parse.http.t6(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 47),        Url.parse.after_scheme("http", t), None{})def Url.parse.http.t5(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 47),        Url.parse.http.t6(t), None{})def Url.parse.http.t4(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 58),        Url.parse.http.t5(t), Url.parse.https.t6(t))def Url.parse.http.t3(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 112),        Url.parse.http.t4(t), None{})def Url.parse.http.t2(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 116),        Url.parse.http.t3(t), None{})def Url.parse.http.t(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 116),        Url.parse.http.t2(t), None{})# Parse one complete absolute HTTP(S) URL. Scheme matching is lowercase and# exact. No whitespace, fragment, percent decoding, or DNS validation is# imposed beyond the no-userinfo host rule.def Url.parse(s: String) -> Maybe<&2, Url>:  match s:    case SNil{}:      None{}    case SCon{Chr{+x}, +t}:      Bool.pick(Maybe<&2, Url>, U32.is_eq(x, 104),        Url.parse.http.t(t), None{})# Render the four fields in the v1 wire form. An empty query omits '?'.def Url.to_string.query(+head: String, query: String) -> String:  match query:    case SNil{}:      head    case SCon{h, t}:      String.append(head, String.append("?", SCon{h, t}))def Url.to_string(u: Url) -> String:  match u:    case Url{scheme, host, path, query}:      +head = String.append(        String.append(String.append(scheme, "://"), host), path)      Url.to_string.query(head, query)def Url.build(u: Url) -> String:  Url.to_string(u)def Url.show(u: Url) -> String:  Url.to_string(u)# Join one non-empty relative path segment to an existing URL path. A segment# beginning with '/' is not stripped; callers should pass "users", not "/users".def Url.join_path.finish(  +acc: String,  +segment: String,  last_slash: Bool) -> String:  match last_slash:    case True{}:      String.append(String.reverse(acc), segment)    case False{}:      String.append(String.reverse(acc), String.append("/", segment))def Url.join_path.scan(  path: String,  +segment: String,  +acc: String,  last_slash: Bool) -> String:  match path:    case SNil{}:      Url.join_path.finish(acc, segment, last_slash)    case SCon{+h, +t}:      Bool.pick(        String,        Char.is_eq(h, '/'),        Url.join_path.scan(t, segment, SCon{h, acc}, True{}),        Url.join_path.scan(t, segment, SCon{h, acc}, False{})      )def Url.join_path.path(path: String, +segment: String) -> String:  Url.join_path.scan(path, segment, SNil{}, False{})def Url.join_path.segment(  scheme: String,  host: String,  path: String,  query: String,  +segment: String) -> Url:  match segment:    case SNil{}:      Url{scheme, host, path, query}    case SCon{+h, +t}:      Url{        scheme,        host,        Url.join_path.path(path, SCon{h, t}),        query      }def Url.join_path(  u: Url,  +segment: String) -> Url:  match u:    case Url{scheme, host, path, query}:      Url.join_path.segment(scheme, host, path, query, segment)