target.bend

bend-kit-router@0.2.0.0 · 221 lines · 35 definitions

1import Base
2import bend-kit-url@0.4.1.0/url.bend as Url
3
4type Query is Data:
5  Query{key: String, value: String}
6
7type Target is Data:
8  Target{segments: List<&2, String>, query: List<&2, Query>, authority: Maybe<&2, String>, star: Bool}
9
10type Utf is Data:
11  Utf{need: U32, lo: U32, hi: U32, cp: U32, out: String}
12
13def between(+c: U32, lo: U32, hi: U32) -> Bool:
14  Bool.and(U32.is_le(lo, c), U32.is_le(c, hi))
15
16def utf.lead(+c: U32, out: String, ascii: Bool) -> Maybe<&2, Utf>:
17  match ascii:
18    case True{}:
19      Some{Utf{0, 128, 191, 0, SCon{Chr{c}, out}}}
20    case False{}:
21      +valid = between(c, 194, 244)
22      +need = Bool.pick(U32, U32.is_lt(c, 224), 1, Bool.pick(U32, U32.is_lt(c, 240), 2, 3))
23      +lo = Bool.pick(U32, U32.is_eq(c, 224), 160, Bool.pick(U32, U32.is_eq(c, 240), 144, 128))
24      +hi = Bool.pick(U32, U32.is_eq(c, 237), 159, Bool.pick(U32, U32.is_eq(c, 244), 143, 191))
25      mask = Bool.pick(U32, U32.is_eq(need, 1), 31, Bool.pick(U32, U32.is_eq(need, 2), 15, 7))
26      Bool.pick(Maybe<&2, Utf>, valid, Some{Utf{need, lo, hi, (c .&. mask : U32), out}}, None{})
27
28def utf.more(ok: Bool, +need: U32, +cp: U32, +out: String) -> Maybe<&2, Utf>:
29  match ok:
30    case False{}:
31      None{}
32    case True{}:
33      Some{Utf{(need - 1 : U32), 128, 191, cp, Bool.pick(String, U32.is_eq(need, 1), SCon{Chr{cp}, out}, out)}}
34
35def utf.step(idle: Bool, +c: U32, need: U32, lo: U32, hi: U32, cp: U32, out: String) -> Maybe<&2, Utf>:
36  match idle:
37    case True{}:
38      utf.lead(c, out, U32.is_lt(c, 128))
39    case False{}:
40      utf.more(between(c, lo, hi), need, (cp * 64 + (c .&. 63 : U32) : U32), out)
41
42def utf.finish(st: Maybe<&2, Utf>) -> Maybe<&2, String>:
43  match st:
44    case None{}:
45      None{}
46    case Some{Utf{need, lo, hi, cp, out}}:
47      Bool.pick(Maybe<&2, String>, U32.is_eq(need, 0), Some{String.reverse(out)}, None{})
48
49def utf.go(s: String, st: Maybe<&2, Utf>) -> Maybe<&2, String>:
50  match s:
51    case SNil{}:
52      utf.finish(st)
53    case SCon{Chr{+c}, t}:
54      match st:
55        case None{}:
56          None{}
57        case Some{Utf{+need, lo, hi, cp, out}}:
58          utf.go(t, utf.step(U32.is_eq(need, 0), c, need, lo, hi, cp, out))
59
60def text.valid(s: String, +slash: Bool) -> Bool:
61  match s:
62    case SNil{}:
63      True{}
64    case SCon{Chr{+c}, t}:
65      Bool.and(Bool.and(U32.is_ne(c, 0), Bool.not(Bool.and(slash, U32.is_eq(c, 47)))), text.valid(t, slash))
66
67def decoded.valid(+s: String, slash: Bool) -> Maybe<&2, String>:
68  Bool.pick(Maybe<&2, String>, text.valid(s, slash), Some{s}, None{})
69
70def decoded(st: Maybe<&2, String>, slash: Bool) -> Maybe<&2, String>:
71  match st:
72    case None{}:
73      None{}
74    case Some{s}:
75      decoded.valid(s, slash)
76
77def percent.got(st: Maybe<&2, String>, slash: Bool) -> Maybe<&2, String>:
78  match st:
79    case None{}:
80      None{}
81    case Some{s}:
82      decoded(utf.go(s, Some{Utf{0, 128, 191, 0, ""}}), slash)
83
84def percent(s: String, slash: Bool) -> Maybe<&2, String>:
85  percent.got(Url.pct.decode(s), slash)
86
87def segments(xs: List<&2, String>) -> Maybe<&2, List<&2, String>>:
88  match xs:
89    case Nil{}:
90      Some{Nil{}}
91    case Con{h, t}:
92      do Maybe<&2, List<&2, String>>:
93        head : String <- percent(h, True{})
94        tail : List<&2, String> <- segments(t)
95        return head <> tail
96
97def split.path(+path: String) -> List<&2, String>:
98  Bool.pick(List<&2, String>, String.eq(path, "/"), Nil{}, String.split(String.drop(path, 1n), '/'))
99
100def plus(s: String) -> String:
101  match s:
102    case SNil{}:
103      SNil{}
104    case SCon{Chr{+c}, t}:
105      SCon{Chr{Bool.pick(U32, U32.is_eq(c, 43), 32, c)}, plus(t)}
106
107def query.pair(pair: String & String) -> Maybe<&2, Query>:
108  (key, value) = pair
109  do Maybe<&2, Query>:
110    k : String <- percent(plus(key), False{})
111    v : String <- percent(plus(value), False{})
112    return Query{k, v}
113
114def query.field(field: String) -> Maybe<&2, Query>:
115  query.pair(Url.take_to(field, 61))
116
117def query(xs: List<&2, String>, acc: List<&2, Query>) -> Maybe<&2, List<&2, Query>>:
118  match xs:
119    case Nil{}:
120      Some{List.reverse(&2, Query, acc)}
121    case "" <> t:
122      query(t, acc)
123    case h <> t:
124      do Maybe<&2, List<&2, Query>>:
125        pair : Query <- query.field(h)
126        query(t, pair <> acc)
127
128def raw.valid(s: String) -> Bool:
129  match s:
130    case SNil{}:
131      True{}
132    case SCon{Chr{+c}, t}:
133      Bool.and(Bool.and(between(c, 33, 126), U32.is_ne(c, 35)), raw.valid(t))
134
135def origin(path: String, q: String, authority: Maybe<&2, String>) -> Maybe<&2, Target>:
136  do Maybe<&2, Target>:
137    ss : List<&2, String> <- segments(split.path(path))
138    pairs : List<&2, Query> <- query(String.split(q, '&'), Nil{})
139    return Target{ss, pairs, authority, False{}}
140
141def origin.split(pair: String & String, authority: Maybe<&2, String>) -> Maybe<&2, Target>:
142  (path, q) = pair
143  origin(path, q, authority)
144
145def alpha(+c: U32) -> Bool:
146  Bool.or(between(c, 65, 90), between(c, 97, 122))
147
148def scheme.tail(s: String) -> Bool:
149  match s:
150    case SNil{}:
151      True{}
152    case SCon{Chr{+c}, t}:
153      Bool.and(Bool.or(alpha(c), Bool.or(between(c, 48, 57), Bool.or(U32.is_eq(c, 43), Bool.or(U32.is_eq(c, 45), U32.is_eq(c, 46))))), scheme.tail(t))
154
155def scheme(s: String) -> Bool:
156  match s:
157    case SNil{}:
158      False{}
159    case SCon{Chr{c}, t}:
160      Bool.and(alpha(c), scheme.tail(t))
161
162def authority.take(s: String, acc: String) -> String & String:
163  match s:
164    case SNil{}:
165      (String.reverse(acc), "/")
166    case SCon{Chr{47}, t}:
167      (String.reverse(acc), SCon{Chr{47}, t})
168    case SCon{Chr{63}, t}:
169      (String.reverse(acc), SCon{Chr{47}, SCon{Chr{63}, t}})
170    case SCon{h, t}:
171      authority.take(t, SCon{h, acc})
172
173def absolute.authority(ok: Bool, authority: String, path: String) -> Maybe<&2, Target>:
174  match ok:
175    case False{}:
176      None{}
177    case True{}:
178      origin.split(Url.take_to(path, 63), Some{authority})
179
180def absolute.taken(pair: String & String) -> Maybe<&2, Target>:
181  (+host, path) = pair
182  absolute.authority(Bool.not(String.is_empty(host)), host, path)
183
184def absolute.rest(rest: String) -> Maybe<&2, Target>:
185  match rest:
186    case SCon{Chr{47}, SCon{Chr{47}, t}}:
187      absolute.taken(authority.take(t, ""))
188    case _:
189      None{}
190
191def absolute.scheme(ok: Bool, rest: String) -> Maybe<&2, Target>:
192  match ok:
193    case False{}:
194      None{}
195    case True{}:
196      absolute.rest(rest)
197
198def absolute.split(pair: String & String) -> Maybe<&2, Target>:
199  (+protocol, rest) = pair
200  absolute.scheme(scheme(protocol), rest)
201
202def parse.form(target: String, method: String) -> Maybe<&2, Target>:
203  match target:
204    case "*":
205      Bool.pick(Maybe<&2, Target>, String.eq(method, "OPTIONS"), Some{Target{Nil{}, Nil{}, None{}, True{}}}, None{})
206    case SCon{Chr{47}, t}:
207      origin.split(Url.take_to(SCon{Chr{47}, t}, 63), None{})
208    case SNil{}:
209      None{}
210    case SCon{h, t}:
211      absolute.split(Url.take_to(SCon{h, t}, 58))
212
213def parse.valid(ok: Bool, target: String, method: String) -> Maybe<&2, Target>:
214  match ok:
215    case False{}:
216      None{}
217    case True{}:
218      parse.form(target, method)
219
220def parse(+target: String, method: String) -> Maybe<&2, Target>:
221  parse.valid(raw.valid(target), target, method)