mt-lang 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/docs/index.html +4 -3
- data/lib/milk_tea/base.rb +1 -1
- data/std/asset_pack.mt +267 -0
- data/std/async/libuv_runtime.mt +539 -0
- data/std/async/mailbox.mt +191 -0
- data/std/async/runtime.mt +87 -0
- data/std/async.mt +87 -0
- data/std/base64.mt +185 -0
- data/std/behavior_tree.mt +396 -0
- data/std/binary.mt +314 -0
- data/std/binary_heap.mt +157 -0
- data/std/bitset.mt +239 -0
- data/std/box2d.mt +535 -0
- data/std/bytes.mt +46 -0
- data/std/c/box2d.mt +1254 -0
- data/std/c/cgltf.mt +647 -0
- data/std/c/cjson.mt +106 -0
- data/std/c/crypto.mt +11 -0
- data/std/c/crypto_support.h +51 -0
- data/std/c/ctype.mt +19 -0
- data/std/c/ctype_bindgen.h +29 -0
- data/std/c/curl.mt +1223 -0
- data/std/c/enet.mt +473 -0
- data/std/c/errno.mt +41 -0
- data/std/c/errno_bindgen.h +53 -0
- data/std/c/flecs.mt +2945 -0
- data/std/c/fs.linux.mt +43 -0
- data/std/c/fs.windows.mt +43 -0
- data/std/c/fs_support.h +954 -0
- data/std/c/gl.mt +2065 -0
- data/std/c/gl_registry_helpers.h +10748 -0
- data/std/c/glfw.mt +499 -0
- data/std/c/libc.mt +167 -0
- data/std/c/libuv.mt +1491 -0
- data/std/c/math.mt +22 -0
- data/std/c/math_bindgen.h +31 -0
- data/std/c/miniaudio.mt +3420 -0
- data/std/c/pcre2.mt +735 -0
- data/std/c/process.mt +58 -0
- data/std/c/process_support.h +1147 -0
- data/std/c/raygui.mt +1602 -0
- data/std/c/raylib.mt +1245 -0
- data/std/c/raymath.mt +163 -0
- data/std/c/rlgl.mt +434 -0
- data/std/c/rpng.mt +60 -0
- data/std/c/rres.mt +187 -0
- data/std/c/sdl3.mt +4132 -0
- data/std/c/sqlite3.mt +951 -0
- data/std/c/stb_image.mt +52 -0
- data/std/c/stb_image_resize2.mt +128 -0
- data/std/c/stb_image_write.mt +19 -0
- data/std/c/stb_rect_pack.mt +38 -0
- data/std/c/stb_truetype.mt +175 -0
- data/std/c/stb_vorbis.mt +31 -0
- data/std/c/stdio.mt +90 -0
- data/std/c/steamworks.h +5769 -0
- data/std/c/steamworks.mt +4771 -0
- data/std/c/string.mt +16 -0
- data/std/c/string_bindgen.h +36 -0
- data/std/c/sync.mt +33 -0
- data/std/c/sync_support.h +219 -0
- data/std/c/terminal.mt +24 -0
- data/std/c/terminal_support.h +408 -0
- data/std/c/time.mt +26 -0
- data/std/c/tls.mt +24 -0
- data/std/c/tls_support.h +626 -0
- data/std/c/tracy.mt +53 -0
- data/std/c/zlib.mt +20 -0
- data/std/c/zlib_support.h +239 -0
- data/std/c/zstd.mt +231 -0
- data/std/cell.mt +60 -0
- data/std/cgltf.mt +115 -0
- data/std/cjson.mt +90 -0
- data/std/cli.mt +768 -0
- data/std/cookie.mt +198 -0
- data/std/counter.mt +160 -0
- data/std/crypto.mt +74 -0
- data/std/cstring.mt +13 -0
- data/std/ctype.mt +52 -0
- data/std/curl/runtime.mt +261 -0
- data/std/curl.mt +38 -0
- data/std/deque.mt +348 -0
- data/std/encoding.mt +93 -0
- data/std/enet.mt +133 -0
- data/std/env.mt +41 -0
- data/std/errno.mt +47 -0
- data/std/flecs.mt +978 -0
- data/std/fmt.mt +271 -0
- data/std/fs.linux.mt +696 -0
- data/std/fs.windows.mt +711 -0
- data/std/fsm.mt +267 -0
- data/std/gl.mt +2062 -0
- data/std/glfw.mt +471 -0
- data/std/goap.mt +342 -0
- data/std/graph.mt +521 -0
- data/std/gzip.mt +77 -0
- data/std/hash.mt +429 -0
- data/std/http/server.mt +810 -0
- data/std/http.mt +1227 -0
- data/std/intern.mt +53 -0
- data/std/jobs.mt +288 -0
- data/std/json.mt +627 -0
- data/std/libc.mt +34 -0
- data/std/libuv.mt +554 -0
- data/std/linear_algebra.mt +243 -0
- data/std/linked_map.mt +372 -0
- data/std/linked_map_view.mt +49 -0
- data/std/linked_set.mt +117 -0
- data/std/log.mt +69 -0
- data/std/map.mt +418 -0
- data/std/math.mt +24 -0
- data/std/mem/arena.mt +137 -0
- data/std/mem/endian.mt +51 -0
- data/std/mem/heap.mt +298 -0
- data/std/mem/pool.mt +164 -0
- data/std/mem/stack.mt +47 -0
- data/std/mem/tracking.mt +119 -0
- data/std/miniaudio.mt +1299 -0
- data/std/multiset.mt +191 -0
- data/std/net/channel.mt +727 -0
- data/std/net/clock.mt +237 -0
- data/std/net/discovery.mt +277 -0
- data/std/net/lobby.mt +813 -0
- data/std/net/manager.mt +539 -0
- data/std/net/mux.mt +839 -0
- data/std/net/nat.mt +122 -0
- data/std/net/packet.mt +219 -0
- data/std/net/punch.mt +162 -0
- data/std/net/rpc.mt +119 -0
- data/std/net/session.mt +1157 -0
- data/std/net/stun.mt +252 -0
- data/std/net/sync.mt +183 -0
- data/std/net/turn.mt +459 -0
- data/std/net.mt +2944 -0
- data/std/oauth2.mt +408 -0
- data/std/option.mt +51 -0
- data/std/ordered_map.mt +497 -0
- data/std/ordered_set.mt +348 -0
- data/std/path.mt +368 -0
- data/std/pcre2/runtime.mt +35 -0
- data/std/pcre2.mt +78 -0
- data/std/priority_queue.mt +55 -0
- data/std/process.mt +837 -0
- data/std/queue.mt +55 -0
- data/std/random.mt +146 -0
- data/std/raygui.mt +99 -0
- data/std/raylib/debug_console.mt +71 -0
- data/std/raylib/easing.mt +388 -0
- data/std/raylib/packed_assets.mt +255 -0
- data/std/raylib/runtime.mt +30 -0
- data/std/raylib/tracy_gpu.mt +37 -0
- data/std/raylib.mt +1510 -0
- data/std/raymath.mt +153 -0
- data/std/result.mt +89 -0
- data/std/rlgl.mt +268 -0
- data/std/rpng.mt +46 -0
- data/std/rres.mt +36 -0
- data/std/sdl3/runtime.mt +54 -0
- data/std/sdl3.mt +1731 -0
- data/std/serialize.mt +68 -0
- data/std/set.mt +124 -0
- data/std/spatial.mt +176 -0
- data/std/sqlite3.mt +151 -0
- data/std/stack.mt +55 -0
- data/std/stb_image.mt +47 -0
- data/std/stb_image_resize2.mt +41 -0
- data/std/stb_image_write.mt +17 -0
- data/std/stb_rect_pack.mt +15 -0
- data/std/stb_truetype.mt +77 -0
- data/std/stb_vorbis.mt +16 -0
- data/std/stdio.mt +88 -0
- data/std/steamworks.mt +1542 -0
- data/std/str.mt +293 -0
- data/std/string.mt +234 -0
- data/std/sync.mt +194 -0
- data/std/tar.mt +704 -0
- data/std/terminal.mt +1002 -0
- data/std/testing.mt +266 -0
- data/std/thread.mt +120 -0
- data/std/time.mt +105 -0
- data/std/tls.mt +616 -0
- data/std/toml.mt +1310 -0
- data/std/tracy.mt +42 -0
- data/std/uri.mt +118 -0
- data/std/url.mt +372 -0
- data/std/vec.mt +433 -0
- data/std/zstd.mt +94 -0
- metadata +187 -2
data/std/oauth2.mt
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
import std.base64 as base64
|
|
2
|
+
import std.bytes as bytes
|
|
3
|
+
import std.crypto as crypto
|
|
4
|
+
import std.http as http
|
|
5
|
+
import std.json as json
|
|
6
|
+
import std.str as text
|
|
7
|
+
import std.string as string
|
|
8
|
+
import std.url as url
|
|
9
|
+
import std.vec as vec
|
|
10
|
+
|
|
11
|
+
public struct Config:
|
|
12
|
+
client_id: string.String
|
|
13
|
+
client_secret: string.String
|
|
14
|
+
authorize_url: string.String
|
|
15
|
+
token_url: string.String
|
|
16
|
+
redirect_uri: string.String
|
|
17
|
+
scopes: vec.Vec[string.String]
|
|
18
|
+
|
|
19
|
+
public struct Tokens:
|
|
20
|
+
access_token: string.String
|
|
21
|
+
refresh_token: Option[string.String]
|
|
22
|
+
token_type: string.String
|
|
23
|
+
expires_in: Option[ptr_int]
|
|
24
|
+
id_token: Option[string.String]
|
|
25
|
+
|
|
26
|
+
public struct Error:
|
|
27
|
+
message: string.String
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
function oauth_error(message: str) -> Error:
|
|
31
|
+
return Error(message = string.String.from_str(message))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
public function create_config(
|
|
35
|
+
client_id: str,
|
|
36
|
+
client_secret: str,
|
|
37
|
+
authorize_url: str,
|
|
38
|
+
token_url: str,
|
|
39
|
+
redirect_uri: str,
|
|
40
|
+
scopes: span[str]
|
|
41
|
+
) -> Config:
|
|
42
|
+
var scope_vec = vec.Vec[string.String].create()
|
|
43
|
+
var index: ptr_uint = 0
|
|
44
|
+
while index < scopes.len:
|
|
45
|
+
scope_vec.push(string.String.from_str(unsafe: read(scopes.data + index)))
|
|
46
|
+
index += 1
|
|
47
|
+
|
|
48
|
+
return Config(
|
|
49
|
+
client_id = string.String.from_str(client_id),
|
|
50
|
+
client_secret = string.String.from_str(client_secret),
|
|
51
|
+
authorize_url = string.String.from_str(authorize_url),
|
|
52
|
+
token_url = string.String.from_str(token_url),
|
|
53
|
+
redirect_uri = string.String.from_str(redirect_uri),
|
|
54
|
+
scopes = scope_vec
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
public function generate_state() -> Result[string.String, Error]:
|
|
59
|
+
let random_result = crypto.random_bytes(32)
|
|
60
|
+
match random_result:
|
|
61
|
+
Result.failure:
|
|
62
|
+
return Result[string.String, Error].failure(error = oauth_error("failed to generate state"))
|
|
63
|
+
Result.success as ok_payload:
|
|
64
|
+
var random_data = ok_payload.value
|
|
65
|
+
let state = base64.encode_urlsafe(random_data.as_span())
|
|
66
|
+
random_data.release()
|
|
67
|
+
return Result[string.String, Error].success(value = state)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
public function generate_pkce() -> Result[PkcePair, Error]:
|
|
71
|
+
let random_result = crypto.random_bytes(32)
|
|
72
|
+
match random_result:
|
|
73
|
+
Result.failure:
|
|
74
|
+
return Result[PkcePair, Error].failure(error = oauth_error("failed to generate PKCE verifier"))
|
|
75
|
+
Result.success as ok_payload:
|
|
76
|
+
var random_data = ok_payload.value
|
|
77
|
+
let verifier = base64.encode_urlsafe(random_data.as_span())
|
|
78
|
+
random_data.release()
|
|
79
|
+
|
|
80
|
+
var challenge = crypto.sha256(text.as_byte_span(verifier.as_str()))
|
|
81
|
+
defer challenge.release()
|
|
82
|
+
|
|
83
|
+
let challenge_b64 = base64.encode_urlsafe(challenge.as_span())
|
|
84
|
+
|
|
85
|
+
return Result[PkcePair, Error].success(
|
|
86
|
+
value = PkcePair(
|
|
87
|
+
verifier = verifier,
|
|
88
|
+
challenge = challenge_b64
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
public struct PkcePair:
|
|
93
|
+
verifier: string.String
|
|
94
|
+
challenge: string.String
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
public function build_authorization_url(
|
|
98
|
+
config: Config,
|
|
99
|
+
state: str,
|
|
100
|
+
pkce_challenge: Option[str]
|
|
101
|
+
) -> string.String:
|
|
102
|
+
var result = string.String.from_str(config.authorize_url.as_str())
|
|
103
|
+
|
|
104
|
+
result.push_byte(63)
|
|
105
|
+
|
|
106
|
+
append_query_param(ref_of(result), "response_type", "code")
|
|
107
|
+
append_query_param(ref_of(result), "client_id", config.client_id.as_str())
|
|
108
|
+
append_query_param(ref_of(result), "redirect_uri", config.redirect_uri.as_str())
|
|
109
|
+
append_query_param(ref_of(result), "state", state)
|
|
110
|
+
|
|
111
|
+
if config.scopes.len() > 0:
|
|
112
|
+
var scope_str = string.String.create()
|
|
113
|
+
var index: ptr_uint = 0
|
|
114
|
+
while index < config.scopes.len():
|
|
115
|
+
if index > 0:
|
|
116
|
+
scope_str.push_byte(32)
|
|
117
|
+
|
|
118
|
+
let name_ptr = config.scopes.get(index) else:
|
|
119
|
+
break
|
|
120
|
+
|
|
121
|
+
scope_str.append(unsafe: read(name_ptr).as_str())
|
|
122
|
+
index += 1
|
|
123
|
+
|
|
124
|
+
append_query_param(ref_of(result), "scope", scope_str.as_str())
|
|
125
|
+
scope_str.release()
|
|
126
|
+
|
|
127
|
+
match pkce_challenge:
|
|
128
|
+
Option.none:
|
|
129
|
+
pass
|
|
130
|
+
Option.some as payload:
|
|
131
|
+
append_query_param(ref_of(result), "code_challenge", payload.value)
|
|
132
|
+
append_query_param(ref_of(result), "code_challenge_method", "S256")
|
|
133
|
+
|
|
134
|
+
return result
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
function append_query_param(target: ref[string.String], key: str, value: str) -> void:
|
|
138
|
+
if target.len() > 0:
|
|
139
|
+
let last_byte = target.as_str().byte_at(target.len() - 1)
|
|
140
|
+
if last_byte != 63 and last_byte != 38:
|
|
141
|
+
target.push_byte(38)
|
|
142
|
+
|
|
143
|
+
var encoded_key = url.percent_encode(key)
|
|
144
|
+
defer encoded_key.release()
|
|
145
|
+
var encoded_value = url.percent_encode(value)
|
|
146
|
+
defer encoded_value.release()
|
|
147
|
+
|
|
148
|
+
target.append(encoded_key.as_str())
|
|
149
|
+
target.push_byte(61)
|
|
150
|
+
target.append(encoded_value.as_str())
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
public async function exchange_code(
|
|
154
|
+
config: Config,
|
|
155
|
+
code: str,
|
|
156
|
+
pkce_verifier: Option[str]
|
|
157
|
+
) -> Result[Tokens, Error]:
|
|
158
|
+
var body_params = vec.Vec[url.FormField].create()
|
|
159
|
+
defer release_form_fields(ref_of(body_params))
|
|
160
|
+
|
|
161
|
+
body_params.push(url.FormField(key = "grant_type", value = "authorization_code"))
|
|
162
|
+
body_params.push(url.FormField(key = "code", value = code))
|
|
163
|
+
body_params.push(url.FormField(key = "redirect_uri", value = config.redirect_uri.as_str()))
|
|
164
|
+
body_params.push(url.FormField(key = "client_id", value = config.client_id.as_str()))
|
|
165
|
+
|
|
166
|
+
match pkce_verifier:
|
|
167
|
+
Option.none:
|
|
168
|
+
pass
|
|
169
|
+
Option.some as payload:
|
|
170
|
+
body_params.push(url.FormField(key = "code_verifier", value = payload.value))
|
|
171
|
+
|
|
172
|
+
var form_body = url.encode_form(body_params.as_span())
|
|
173
|
+
defer form_body.release()
|
|
174
|
+
|
|
175
|
+
var auth_header = build_basic_auth_header(config.client_id.as_str(), config.client_secret.as_str())
|
|
176
|
+
defer auth_header.release()
|
|
177
|
+
|
|
178
|
+
var headers = array[http.RequestHeader, 2](
|
|
179
|
+
http.RequestHeader(name = "Content-Type", value = "application/x-www-form-urlencoded"),
|
|
180
|
+
http.RequestHeader(name = "Authorization", value = auth_header.as_str())
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
let header_span = span[http.RequestHeader](data = ptr_of(headers[0]), len = 2)
|
|
184
|
+
let body_span = text.as_byte_span(form_body.as_str())
|
|
185
|
+
|
|
186
|
+
let request_result = await http.request(
|
|
187
|
+
config.token_url.as_str(),
|
|
188
|
+
"POST",
|
|
189
|
+
header_span,
|
|
190
|
+
Option[span[ubyte]].some(value = body_span)
|
|
191
|
+
)
|
|
192
|
+
match request_result:
|
|
193
|
+
Result.failure as payload:
|
|
194
|
+
return Result[Tokens, Error].failure(error = http_error_to_oauth(payload.error))
|
|
195
|
+
Result.success as payload:
|
|
196
|
+
var response = payload.value
|
|
197
|
+
defer response.release()
|
|
198
|
+
|
|
199
|
+
let body_str = response.body_as_str() else:
|
|
200
|
+
return Result[Tokens, Error].failure(error = oauth_error("token response body is not valid UTF-8"))
|
|
201
|
+
|
|
202
|
+
return parse_token_response(body_str, response.status_code)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
function build_basic_auth_header(client_id: str, client_secret: str) -> string.String:
|
|
206
|
+
var credentials = string.String.with_capacity(client_id.len + client_secret.len + 2)
|
|
207
|
+
credentials.append(client_id)
|
|
208
|
+
credentials.push_byte(58)
|
|
209
|
+
credentials.append(client_secret)
|
|
210
|
+
|
|
211
|
+
var encoded = base64.encode(text.as_byte_span(credentials.as_str()))
|
|
212
|
+
credentials.release()
|
|
213
|
+
|
|
214
|
+
var header = string.String.with_capacity(encoded.len() + 7)
|
|
215
|
+
header.append("Basic ")
|
|
216
|
+
header.append(encoded.as_str())
|
|
217
|
+
|
|
218
|
+
encoded.release()
|
|
219
|
+
return header
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
function parse_token_response(body_str: str, status_code: int) -> Result[Tokens, Error]:
|
|
223
|
+
let parsed_json = json.parse(body_str)
|
|
224
|
+
match parsed_json:
|
|
225
|
+
Result.failure:
|
|
226
|
+
return Result[Tokens, Error].failure(error = oauth_error("token response is not valid JSON"))
|
|
227
|
+
Result.success as json_payload:
|
|
228
|
+
let json_value = json_payload.value
|
|
229
|
+
defer json.release_value(json_value)
|
|
230
|
+
|
|
231
|
+
if status_code != 200:
|
|
232
|
+
let object_ptr = json_value.as_object() else:
|
|
233
|
+
return Result[Tokens, Error].failure(error = oauth_error("token error response is not an object"))
|
|
234
|
+
|
|
235
|
+
let error_desc = unsafe: read(object_ptr).get_string("error_description")
|
|
236
|
+
match error_desc:
|
|
237
|
+
Option.some as payload:
|
|
238
|
+
let msg = string.String.from_str(payload.value)
|
|
239
|
+
return Result[Tokens, Error].failure(error = Error(message = msg))
|
|
240
|
+
Option.none:
|
|
241
|
+
return Result[
|
|
242
|
+
Tokens,
|
|
243
|
+
Error
|
|
244
|
+
].failure(error = oauth_error("token endpoint returned error status"))
|
|
245
|
+
|
|
246
|
+
let object_ptr = json_value.as_object() else:
|
|
247
|
+
return Result[Tokens, Error].failure(error = oauth_error("token response is not an object"))
|
|
248
|
+
|
|
249
|
+
unsafe:
|
|
250
|
+
let access_token_opt = read(object_ptr).get_string("access_token")
|
|
251
|
+
let access_token = access_token_opt else:
|
|
252
|
+
return Result[Tokens, Error].failure(error = oauth_error("token response missing access_token"))
|
|
253
|
+
|
|
254
|
+
let token_type = read_json_string(read(object_ptr), "token_type")
|
|
255
|
+
var token_type_str = string.String.from_str("Bearer")
|
|
256
|
+
match token_type:
|
|
257
|
+
Option.some as payload:
|
|
258
|
+
let owned = string.String.from_str(payload.value)
|
|
259
|
+
token_type_str = owned
|
|
260
|
+
Option.none:
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
var refresh_token: Option[string.String] = Option[string.String].none
|
|
264
|
+
let refresh_opt = read_json_string(read(object_ptr), "refresh_token")
|
|
265
|
+
match refresh_opt:
|
|
266
|
+
Option.some as payload:
|
|
267
|
+
refresh_token = Option[string.String].some(value = string.String.from_str(payload.value))
|
|
268
|
+
Option.none:
|
|
269
|
+
pass
|
|
270
|
+
|
|
271
|
+
var expires_in: Option[ptr_int] = Option[ptr_int].none
|
|
272
|
+
let expires_opt = read_json_number(read(object_ptr), "expires_in")
|
|
273
|
+
match expires_opt:
|
|
274
|
+
Option.some as payload:
|
|
275
|
+
expires_in = Option[ptr_int].some(value = ptr_int<-payload.value)
|
|
276
|
+
Option.none:
|
|
277
|
+
pass
|
|
278
|
+
|
|
279
|
+
var id_token: Option[string.String] = Option[string.String].none
|
|
280
|
+
let id_opt = read_json_string(read(object_ptr), "id_token")
|
|
281
|
+
match id_opt:
|
|
282
|
+
Option.some as payload:
|
|
283
|
+
id_token = Option[string.String].some(value = string.String.from_str(payload.value))
|
|
284
|
+
Option.none:
|
|
285
|
+
pass
|
|
286
|
+
|
|
287
|
+
return Result[Tokens, Error].success(value = Tokens(
|
|
288
|
+
access_token = string.String.from_str(access_token),
|
|
289
|
+
refresh_token = refresh_token,
|
|
290
|
+
token_type = token_type_str,
|
|
291
|
+
expires_in = expires_in,
|
|
292
|
+
id_token = id_token
|
|
293
|
+
))
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
function read_json_string(object_value: json.Object, key: str) -> Option[str]:
|
|
297
|
+
return object_value.get_string(key)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
function read_json_number(object_value: json.Object, key: str) -> Option[double]:
|
|
301
|
+
return object_value.get_number(key)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
public async function refresh_access_token(
|
|
305
|
+
config: Config,
|
|
306
|
+
refresh_token: str
|
|
307
|
+
) -> Result[Tokens, Error]:
|
|
308
|
+
var body_params = vec.Vec[url.FormField].create()
|
|
309
|
+
defer release_form_fields(ref_of(body_params))
|
|
310
|
+
|
|
311
|
+
body_params.push(url.FormField(key = "grant_type", value = "refresh_token"))
|
|
312
|
+
body_params.push(url.FormField(key = "refresh_token", value = refresh_token))
|
|
313
|
+
body_params.push(url.FormField(key = "client_id", value = config.client_id.as_str()))
|
|
314
|
+
|
|
315
|
+
var form_body = url.encode_form(body_params.as_span())
|
|
316
|
+
defer form_body.release()
|
|
317
|
+
|
|
318
|
+
var auth_header = build_basic_auth_header(config.client_id.as_str(), config.client_secret.as_str())
|
|
319
|
+
defer auth_header.release()
|
|
320
|
+
|
|
321
|
+
var headers = array[http.RequestHeader, 2](
|
|
322
|
+
http.RequestHeader(name = "Content-Type", value = "application/x-www-form-urlencoded"),
|
|
323
|
+
http.RequestHeader(name = "Authorization", value = auth_header.as_str())
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
let header_span = span[http.RequestHeader](data = ptr_of(headers[0]), len = 2)
|
|
327
|
+
let body_span = text.as_byte_span(form_body.as_str())
|
|
328
|
+
|
|
329
|
+
let request_result = await http.request(
|
|
330
|
+
config.token_url.as_str(),
|
|
331
|
+
"POST",
|
|
332
|
+
header_span,
|
|
333
|
+
Option[span[ubyte]].some(value = body_span)
|
|
334
|
+
)
|
|
335
|
+
match request_result:
|
|
336
|
+
Result.failure as payload:
|
|
337
|
+
return Result[Tokens, Error].failure(error = http_error_to_oauth(payload.error))
|
|
338
|
+
Result.success as payload:
|
|
339
|
+
var response = payload.value
|
|
340
|
+
defer response.release()
|
|
341
|
+
|
|
342
|
+
let body_str = response.body_as_str() else:
|
|
343
|
+
return Result[Tokens, Error].failure(error = oauth_error("token response body is not valid UTF-8"))
|
|
344
|
+
|
|
345
|
+
return parse_token_response(body_str, response.status_code)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
function http_error_to_oauth(http_error: http.Error) -> Error:
|
|
349
|
+
var owned_error = http_error
|
|
350
|
+
let message = string.String.from_str(owned_error.message.as_str())
|
|
351
|
+
owned_error.release()
|
|
352
|
+
return Error(message = message)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
function release_form_fields(fields: ref[vec.Vec[url.FormField]]) -> void:
|
|
356
|
+
fields.release()
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
extending Config:
|
|
360
|
+
public editable function release() -> void:
|
|
361
|
+
this.client_id.release()
|
|
362
|
+
this.client_secret.release()
|
|
363
|
+
this.authorize_url.release()
|
|
364
|
+
this.token_url.release()
|
|
365
|
+
this.redirect_uri.release()
|
|
366
|
+
|
|
367
|
+
var index: ptr_uint = 0
|
|
368
|
+
while index < this.scopes.len():
|
|
369
|
+
let scope_ptr = this.scopes.get(index) else:
|
|
370
|
+
break
|
|
371
|
+
|
|
372
|
+
unsafe:
|
|
373
|
+
var scope = read(scope_ptr)
|
|
374
|
+
scope.release()
|
|
375
|
+
|
|
376
|
+
index += 1
|
|
377
|
+
|
|
378
|
+
this.scopes.release()
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
extending Tokens:
|
|
382
|
+
public editable function release() -> void:
|
|
383
|
+
this.access_token.release()
|
|
384
|
+
|
|
385
|
+
match this.refresh_token:
|
|
386
|
+
Option.some as payload:
|
|
387
|
+
payload.value.release()
|
|
388
|
+
Option.none:
|
|
389
|
+
pass
|
|
390
|
+
|
|
391
|
+
this.token_type.release()
|
|
392
|
+
|
|
393
|
+
match this.id_token:
|
|
394
|
+
Option.some as payload:
|
|
395
|
+
payload.value.release()
|
|
396
|
+
Option.none:
|
|
397
|
+
pass
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
extending PkcePair:
|
|
401
|
+
public editable function release() -> void:
|
|
402
|
+
this.verifier.release()
|
|
403
|
+
this.challenge.release()
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
extending Error:
|
|
407
|
+
public editable function release() -> void:
|
|
408
|
+
this.message.release()
|
data/std/option.mt
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Standard library: Option type
|
|
2
|
+
#
|
|
3
|
+
# Option[T] represents an optional value: either Some(T) or None.
|
|
4
|
+
# Imported automatically as part of the language prelude.
|
|
5
|
+
|
|
6
|
+
public variant Option[T]:
|
|
7
|
+
some(value: T)
|
|
8
|
+
none
|
|
9
|
+
|
|
10
|
+
extending Option[T]:
|
|
11
|
+
public function is_some() -> bool:
|
|
12
|
+
match this:
|
|
13
|
+
Option.some:
|
|
14
|
+
return true
|
|
15
|
+
Option.none:
|
|
16
|
+
return false
|
|
17
|
+
|
|
18
|
+
public function is_none() -> bool:
|
|
19
|
+
match this:
|
|
20
|
+
Option.some:
|
|
21
|
+
return false
|
|
22
|
+
Option.none:
|
|
23
|
+
return true
|
|
24
|
+
|
|
25
|
+
public function unwrap() -> T:
|
|
26
|
+
match this:
|
|
27
|
+
Option.some as payload:
|
|
28
|
+
return payload.value
|
|
29
|
+
Option.none:
|
|
30
|
+
fatal(c"called Option.unwrap on a none value")
|
|
31
|
+
|
|
32
|
+
public function expect(msg: str) -> T:
|
|
33
|
+
match this:
|
|
34
|
+
Option.some as payload:
|
|
35
|
+
return payload.value
|
|
36
|
+
Option.none:
|
|
37
|
+
fatal(msg)
|
|
38
|
+
|
|
39
|
+
public function unwrap_or(default: T) -> T:
|
|
40
|
+
match this:
|
|
41
|
+
Option.some as payload:
|
|
42
|
+
return payload.value
|
|
43
|
+
Option.none:
|
|
44
|
+
return default
|
|
45
|
+
|
|
46
|
+
public function unwrap_or_else(f: proc() -> T) -> T:
|
|
47
|
+
match this:
|
|
48
|
+
Option.some as payload:
|
|
49
|
+
return payload.value
|
|
50
|
+
Option.none:
|
|
51
|
+
return f()
|