1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
|
app "roctorrent"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br" }
imports [pf.Stdout]
provides [main] to pf
# Bencoded strings are not character strings i.e. text but binary data
Bytes: (List U8)
# Decodes an exact string, errors if there is remaining unused data
bDecodeStr: Bytes -> Result (Bytes) [Malformatted Str]
bDecodeStr = \i ->
when bDecodeStrAndRem i is
Ok {res, others} ->
if List.len others == 0 then
Ok res
else
Err (Malformatted "bDecodeStr: trailing characters")
Err (Malformatted s) -> Err (Malformatted "bDecodeStr: error calling bDecodeStrAndRem \(s)")
# Decodes a string and returns the remainder of the input as well if there was any
bDecodeStrAndRem: Bytes -> Result {res: Bytes, others: Bytes} [Malformatted Str]
bDecodeStrAndRem = \i ->
when (List.splitFirst i ':') is
Ok {before, after} ->
when (Str.fromUtf8 before) is
Ok ls ->
when (Str.toNat ls) is
Ok l ->
if List.len after >= l then
r2 = (List.split after l)
Ok {res: r2.before, others: r2.others}
else
Err (Malformatted "bDecodeStrAndRem: len exceeds remaining data")
Err _err -> Err (Malformatted "bDecodeStrAndRem: failed to parse number")
Err _err -> Err (Malformatted "bDecodeStrAndRem: failed to parse utf8")
Err _err -> Err (Malformatted "bDecodeStrAndRem: no ':' found")
expect
res = bDecodeStr (Str.toUtf8 "3:foo")
res == Ok (Str.toUtf8 "foo")
expect
errCases = [
{case: "", err: "bDecodeStr: error calling bDecodeStrAndRem bDecodeStrAndRem: no ':' found"},
{case: "foo", err: "bDecodeStr: error calling bDecodeStrAndRem bDecodeStrAndRem: no ':' found"},
{case: "1:foo", err: "bDecodeStr: trailing characters"},
{case: "4:foo",err: "bDecodeStr: error calling bDecodeStrAndRem bDecodeStrAndRem: len exceeds remaining data"},
{case: ":", err: "bDecodeStr: error calling bDecodeStrAndRem bDecodeStrAndRem: failed to parse number"},
{case: "foo:", err: "bDecodeStr: error calling bDecodeStrAndRem bDecodeStrAndRem: failed to parse number"},
{case: "1:",err: "bDecodeStr: error calling bDecodeStrAndRem bDecodeStrAndRem: len exceeds remaining data"},
{case: "::",err: "bDecodeStr: error calling bDecodeStrAndRem bDecodeStrAndRem: failed to parse number"},
]
results = List.map errCases \t ->
when bDecodeStr (Str.toUtf8 t.case) is
Ok _ -> "Malformatted input \(t.case) should have failed!"
Err (Malformatted err) -> err
expected = List.map errCases \t -> t.err
results == expected
# Decodes an exact number, exta input causes an error
bDecodeNum: Bytes -> Result I64 [Malformatted Str]
bDecodeNum = \i ->
when bDecodeNumAndRem i is
Ok {num, rem} ->
if (List.len rem) == 0 then
Ok num
else
Err (Malformatted "remaining characters after number")
Err e -> Err e
# Decodes a number and returns the number and any unused input
bDecodeNumAndRem: Bytes -> Result {num: I64, rem: (Bytes)} [Malformatted Str]
bDecodeNumAndRem = \i ->
when (List.first i) is
Ok f ->
if f == 'i' then
when List.findFirstIndex i (\j -> j == 'e') is
Ok ix ->
{before, others} = List.split i (ix+1)
numl = List.sublist before {start: 1, len: ix-1}
when (Str.fromUtf8 numl) is
Ok nums ->
when (Str.toI64 nums) is
Ok num -> Ok {num: num, rem: others}
Err _e -> Err (Malformatted "failed to parse number from \(nums)")
Err _e -> Err (Malformatted "failed to parse utf8")
Err _e -> Err (Malformatted "failed to find number end char 'e'")
else
Err (Malformatted "number didn't start with 'i'")
Err _e -> Err (Malformatted "number string was empty")
expect
res = bDecodeNum (Str.toUtf8 "i32e")
res == Ok 32i64
expect
errCases = [
{case: "", msg: "number string was empty"},
{case: "foo", msg: "number didn't start with 'i'"},
{case: "i32", msg: "failed to find number end char 'e'"},
{case: "32e", msg: "number didn't start with 'i'"},
{case: "ifooe", msg: "failed to parse number from foo"},
{case: "ie", msg: "failed to parse number from "},
{case: "i", msg: "failed to find number end char 'e'"},
{case: "e", msg: "number didn't start with 'i'"},
{case: "i522222222222222222343423432434322e", msg: "failed to parse number from 522222222222222222343423432434322"},
]
List.all errCases \{case, msg} ->
res = bDecodeNum (Str.toUtf8 case)
res == Err (Malformatted msg)
# Tags for storing heterogenous collections
BVal: [
BStr (Bytes),
BNum I64,
BList (List BVal),
BDict (Dict (Bytes) BVal)
]
# Detects and decodes any kind of element.
bDecodeValueAndRem: (Bytes) -> Result {res: BVal, others: (Bytes)} [Malformatted, End (Bytes)]
bDecodeValueAndRem = \i ->
when (List.first i) is
Ok first ->
when first is
'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' ->
when bDecodeStrAndRem i is
Ok {res, others} ->
Ok {res: BStr res, others: others}
Err _err ->
Err Malformatted
'i' ->
when bDecodeNumAndRem i is
Ok {num, rem} ->
Ok {res: BNum num, others: rem}
Err _err ->
Err Malformatted
'l' ->
when bDecodeListAndRem i is
Ok {res, others} ->
Ok {res: BList res, others: others}
Err _err ->
Err Malformatted
'd' ->
when bDecodeDictAndRem i is
Ok {res, others} ->
Ok {res: BDict res, others: others}
Err _err ->
Err Malformatted
'e' ->
r = List.split i 1
Err (End r.others)
_ ->
Err Malformatted
Err _err -> Err Malformatted
expect
res = bDecodeValueAndRem (Str.toUtf8 "i64efoo")
res == Ok {res: BNum 64, others: (Str.toUtf8 "foo")}
expect
res = bDecodeValueAndRem (Str.toUtf8 "3:foo3:foo")
res == Ok {res: BStr (Str.toUtf8 "foo"), others: (Str.toUtf8 "3:foo")}
expect
res = bDecodeValueAndRem (Str.toUtf8 "efoo")
res == Err (End (Str.toUtf8 "foo"))
expect
res = bDecodeValueAndRem (Str.toUtf8 "z")
res == Err Malformatted
# Decodes a list. List elements can be any
bDecodeList: (Bytes) -> Result (List BVal) [Malformatted]
bDecodeList = \i ->
when bDecodeListAndRem i is
Ok {res, others} ->
if (List.len others) == 0 then
Ok res
else
Err Malformatted
Err e ->
Err e
bDecodeListAndRem: (Bytes) -> Result {res: (List BVal), others: (Bytes)} [Malformatted]
bDecodeListAndRem = \i ->
when List.first i is
Ok f ->
if f == 'l' then
r = List.split i 1
bDecodeListAndRemInternal [] r.others
else
Err Malformatted
Err _e -> Err Malformatted
bDecodeListAndRemInternal: List BVal, Bytes -> Result {res: (List BVal), others: (Bytes)} [Malformatted]
bDecodeListAndRemInternal = \l, i ->
when bDecodeValueAndRem i is
Ok {res, others} ->
l2 = List.append l res
bDecodeListAndRemInternal l2 others
Err e -> when e is
End others ->
Ok {res: l, others: others}
Malformatted ->
Err Malformatted
expect
res = bDecodeList (Str.toUtf8 "le")
res == Ok []
expect
res = bDecodeList (Str.toUtf8 "li123e3:fooe")
res == Ok [BNum 123i64, BStr (Str.toUtf8 "foo")]
expect
res = bDecodeList (Str.toUtf8 "li123e3:fooe123")
res == Err Malformatted
expect
res = bDecodeList (Str.toUtf8 "lli123eee")
res == Ok [BList [BNum 123i64]]
bDecodeDict: (Bytes) -> Result (Dict (Bytes) BVal) [Malformatted Str]
bDecodeDict = \i ->
when bDecodeDictAndRem i is
Ok {res, others} ->
if (List.len others) == 0 then
Ok res
else
Err (Malformatted "bDecodeDict: trailing characters")
Err e ->
Err e
bDecodeDictAndRem: (Bytes) -> Result {res: (Dict (Bytes) BVal), others: (Bytes)} [Malformatted Str]
bDecodeDictAndRem = \i ->
when List.first i is
Ok f ->
if f == 'd' then
r = List.split i 1
bDecodeDictAndRemInternal (Dict.empty{}) r.others
else
Err (Malformatted "first character not 'd'")
Err _e -> Err (Malformatted "empty input")
bDecodeDictAndRemInternal: (Dict (Bytes) BVal), (Bytes) -> Result {res: (Dict (Bytes) BVal), others: (Bytes)} [Malformatted Str]
bDecodeDictAndRemInternal = \d, i ->
when bDecodeValueAndRem i is
Ok r1 ->
when r1.res is
BStr s ->
key = s
when bDecodeValueAndRem r1.others is
Ok r2 ->
val = r2.res
d2 = Dict.insert d key val
bDecodeDictAndRemInternal d2 r2.others
Err e -> when e is
End _ ->
Err (Malformatted "key with no value")
Malformatted ->
Err (Malformatted "bDecodeDictAndRemInternal: error decoding value")
_ -> Err (Malformatted "bDecodeDictAndRemInternal: wrong key type")
Err Malformatted ->
Err (Malformatted "bDecodeDictAndRemInternal: error decoding key")
Err (End others) ->
Ok {res: d, others: others}
expect
expected = Dict.fromList [
((Str.toUtf8 "foo"), (BStr (Str.toUtf8 "bar"))),
]
res = bDecodeDict (Str.toUtf8 "d3:foo3:bare")
res == Ok expected
expect
nest = Dict.fromList [
((Str.toUtf8 "foo"), (BStr (Str.toUtf8 "bar"))),
]
expected = Dict.fromList [
((Str.toUtf8 "top"), (BDict nest)),
]
res = bDecodeDict (Str.toUtf8 "d3:topd3:foo3:baree")
res == Ok expected
main =
Stdout.line "Hello, World"
|