src/url.bend
emerging-ezhttp@0.21.0.0 · 166 lines · 25 definitions
1
2
3
4
5
6
7
8import Base
9
10
11type Loc is Data:
12 Bad{why: String}
13 Loc{scheme: String, host: String, port: U32, path: String}
14
15
16
17def dflt(scheme: String) -> U32:
18 Bool.pick(U32, String.eq(scheme, "https"), 443, 80)
19
20
21def port.num(got: Maybe<&2, U32>, scheme: String) -> U32:
22 match got:
23 case None{}:
24 dflt(scheme)
25 case Some{n}:
26 n
27
28
29def port.tail(ps: List<&2, String>, scheme: String) -> U32:
30 match ps:
31 case []:
32 dflt(scheme)
33 case h <> t:
34 port.num(U32.read(h), scheme)
35
36
37def port.of(ps: List<&2, String>, scheme: String) -> U32:
38 match ps:
39 case []:
40 dflt(scheme)
41 case h <> t:
42 port.tail(t, scheme)
43
44
45def host.of(ps: List<&2, String>) -> String:
46 match ps:
47 case []:
48 ""
49 case h <> t:
50 h
51
52
53
54
55def path.of(ps: List<&2, String>) -> String:
56 match ps:
57 case []:
58 "/"
59 case h <> t:
60 "/" ++ String.join(t, "/")
61
62
63def auth.last.of(last: Maybe<&2, String>) -> String:
64 match last:
65 case None{}:
66 ""
67 case Some{hostport}:
68 hostport
69
70
71def auth.last(ps: List<&2, String>) -> String:
72 auth.last.of(List.last(&2, String, ps))
73
74
75def auth.host(authority: String) -> String:
76 auth.last(String.split(authority, '@'))
77
78
79def frag.drop.of(ps: List<&2, String>) -> String:
80 match ps:
81 case []:
82 ""
83 case hier <> _:
84 hier
85
86
87def frag.drop(s: String) -> String:
88 frag.drop.of(String.split(s, '#'))
89
90
91def query.hier(ps: List<&2, String>) -> String:
92 match ps:
93 case []:
94 ""
95 case hier <> _:
96 hier
97
98
99def query.text(ps: List<&2, String>) -> String:
100 match ps:
101 case []:
102 ""
103 case _ <> rest:
104 String.join(rest, "?")
105
106
107def path.target.of(path: String, query: String, empty: Bool) -> String:
108 match empty:
109 case True{}:
110 path
111 case False{}:
112 path ++ "?" ++ query
113
114
115def path.target(path: String, +query: String) -> String:
116 path.target.of(path, query, String.eq(query, ""))
117
118
119def parse.auth(+scheme: String, +au: List<&2, String>, path: String) -> Loc:
120 Loc{scheme, host.of(au), port.of(au, scheme), path}
121
122
123def parse.parts(+scheme: String, +ps: List<&2, String>, query: String) -> Loc:
124 parse.auth(scheme, String.split(auth.host(host.of(ps)), ':'),
125 path.target(path.of(ps), query))
126
127
128def parse.q(+scheme: String, +ps: List<&2, String>) -> Loc:
129 parse.parts(scheme, String.split(query.hier(ps), '/'), query.text(ps))
130
131
132def parse.hier(+scheme: String, +rest: String) -> Loc:
133 parse.q(scheme, String.split(frag.drop(rest), '?'))
134
135
136def parse.body(+scheme: String, +rest: String, slashes: Bool) -> Loc:
137 match slashes:
138 case False{}:
139 Bad{scheme ++ ":" ++ rest ++ " has no //"}
140 case True{}:
141 parse.hier(scheme, String.drop(rest, 2n))
142
143
144def parse.rest(+scheme: String, +rest: String) -> Loc:
145 parse.body(scheme, rest, String.starts_with(rest, "//"))
146
147
148
149def parse.scheme(ps: List<&2, String>) -> Loc:
150 match ps:
151 case []:
152 Bad{"an empty url"}
153 case h <> t:
154 parse.rest(String.to_lower(h), String.join(t, ":"))
155
156
157def parse(url: String) -> Loc:
158 parse.scheme(String.split(url, ':'))
159
160
161def show(l: Loc) -> String:
162 match l:
163 case Bad{why}:
164 "bad " ++ why
165 case Loc{scheme, host, port, path}:
166 scheme ++ " " ++ host ++ " " ++ U32.show(port) ++ " " ++ path