~/bend-docscommunity

http/query.bend source

http/query.bend on the hub · documented module

import Baseimport ./text.bend as Timport ../utf8/utf8.bend as U# a query string's pairs, in order and with repeats kept: ?tag=a&tag=b is# two pairs, which a map would lose (application/x-www-form-urlencoded)type Param is Data:  Param{key: String, value: String}def hex(+x: U32) -> Maybe<&2, U32>:  Bool.pick(Maybe<&2, U32>, Bool.and(U32.is_ge(x, 48), U32.is_le(x, 57)), Some{(x - 48 : U32)},    Bool.pick(Maybe<&2, U32>, Bool.and(U32.is_ge(x, 97), U32.is_le(x, 102)), Some{(x - 87 : U32)},      Bool.pick(Maybe<&2, U32>, Bool.and(U32.is_ge(x, 65), U32.is_le(x, 70)), Some{(x - 55 : U32)}, None{})))def pct.byte(a: Maybe<&2, U32>, b: Maybe<&2, U32>) -> Maybe<&2, U32>:  match a b:    case Some{+x} Some{+y}:      Some{(x * 16 + y : U32)}    case _ _:      None{}def pct.push(m: Maybe<&2, U32>, +x: U32, +y: U32, out: List<&2, U32>) -> List<&2, U32>:  match m:    case Some{b}:      Con{b, out}    case None{}:      Con{y, Con{x, Con{37, out}}}# the bytes of a component: %XX is a byte, + a space; a % not followed by# two hex digits stays as it isdef bytes.go(s: String, out: List<&2, U32>) -> List<&2, U32>:  match s:    case SNil{}:      List.reverse(&2, U32, out)    case SCon{Chr{37}, SCon{Chr{+x}, SCon{Chr{+y}, t}}}:      bytes.go(t, pct.push(pct.byte(hex(x), hex(y)), x, y, out))    case SCon{Chr{43}, t}:      bytes.go(t, Con{32, out})    case SCon{Chr{x}, t}:      bytes.go(t, U.enc.push(x, out))def text.of(t: U.Text) -> String:  match t:    case U.TOk{s}:      s    case U.TBad{_, before}:      String.reverse(before)# a component decoded: bytes that are not UTF-8 end it where they startdef decode(s: String) -> String:  text.of(U.decode(bytes.go(s, [])))def pair(c: T.Cut) -> Param:  match c:    case T.Cut{k, Some{v}}:      Param{decode(k), decode(v)}    case T.Cut{k, None{}}:      Param{decode(k), ""}def add(p: String, acc: List<&2, Param>) -> List<&2, Param>:  match p:    case SNil{}:      acc    case SCon{c, t}:      Con{pair(T.cut(SCon{c, t}, 61)), acc}# c is the query cut at its first &: the pair before it, and what followsdef parse.go(fuel: Nat, c: T.Cut, acc: List<&2, Param>) -> List<&2, Param>:  match fuel c:    case 1n+f T.Cut{p, Some{rest}}:      parse.go(f, T.cut(rest, 38), add(p, acc))    case _ T.Cut{p, _}:      List.reverse(&2, Param, add(p, acc))# the pairs of a query string, without its ?def parse(+q: String) -> List<&2, Param>:  parse.go(String.length(q), T.cut(q, 38), [])