@kaiko.io/rescript-deser 6.0.1 → 7.0.0-alpha.2
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.
- package/README.md +16 -5
- package/lib/bs/.compiler.log +2 -2
- package/lib/bs/build.ninja +0 -27
- package/lib/bs/compiler-info.json +8 -0
- package/lib/bs/src/Deser.ast +0 -0
- package/lib/bs/src/Deser.cmi +0 -0
- package/lib/bs/src/Deser.cmj +0 -0
- package/lib/bs/src/Deser.cmt +0 -0
- package/lib/bs/src/Deser.res +341 -0
- package/lib/bs/tests/QUnit.ast +0 -0
- package/lib/bs/tests/QUnit.cmi +0 -0
- package/lib/bs/tests/QUnit.cmj +0 -0
- package/lib/bs/tests/QUnit.cmt +0 -0
- package/lib/bs/tests/QUnit.res +72 -0
- package/lib/bs/tests/index.ast +0 -0
- package/lib/bs/tests/index.cmi +0 -0
- package/lib/bs/tests/index.cmj +0 -0
- package/lib/bs/tests/index.cmt +0 -0
- package/lib/bs/tests/index.res +212 -0
- package/lib/es6/src/Deser.js +302 -477
- package/lib/es6/tests/index.js +260 -241
- package/lib/js/src/Deser.js +298 -473
- package/lib/js/tests/index.js +261 -242
- package/lib/ocaml/.compiler.log +2 -0
- package/lib/ocaml/Deser.ast +0 -0
- package/lib/ocaml/Deser.cmi +0 -0
- package/lib/ocaml/Deser.cmj +0 -0
- package/lib/ocaml/Deser.cmt +0 -0
- package/lib/ocaml/Deser.res +341 -0
- package/lib/ocaml/QUnit.ast +0 -0
- package/lib/ocaml/QUnit.cmi +0 -0
- package/lib/ocaml/QUnit.cmj +0 -0
- package/lib/ocaml/QUnit.cmt +0 -0
- package/lib/ocaml/QUnit.res +72 -0
- package/lib/ocaml/index.ast +0 -0
- package/lib/ocaml/index.cmi +0 -0
- package/lib/ocaml/index.cmj +0 -0
- package/lib/ocaml/index.cmt +0 -0
- package/lib/ocaml/index.res +212 -0
- package/lib/rescript.lock +1 -0
- package/package.json +5 -6
- package/rescript.json +4 -6
- package/src/Deser.res +86 -97
- package/tests/QUnit.res +4 -4
- package/tests/index.res +34 -11
- package/tests/run-tests.js +192 -0
- package/yarn.lock +683 -0
- package/lib/bs/.bsbuild +0 -0
- package/lib/bs/.bsdeps +0 -9
- package/lib/bs/.ninja_log +0 -93
- package/lib/bs/.project-files-cache +0 -0
- package/lib/bs/.sourcedirs.json +0 -1
- package/lib/bs/install.ninja +0 -10
- package/lib/bs/src/Deser.d +0 -0
- package/lib/bs/tests/QUnit.d +0 -0
- package/lib/bs/tests/index.d +0 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
open QUnit
|
|
2
|
+
|
|
3
|
+
module Appointment = {
|
|
4
|
+
type t = {
|
|
5
|
+
note: string,
|
|
6
|
+
date: Date.t,
|
|
7
|
+
extra: option<string>,
|
|
8
|
+
}
|
|
9
|
+
module Deserializer = Deser.MakeDeserializer({
|
|
10
|
+
type t = t
|
|
11
|
+
open Deser.Field
|
|
12
|
+
|
|
13
|
+
let fields = Object([("note", String), ("date", Date), ("extra", Optional(String))])
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module_("Basic deserializer", _ => {
|
|
18
|
+
let valid: array<(_, Appointment.t)> = [
|
|
19
|
+
(
|
|
20
|
+
%raw(`{"note": "Bicentennial doctor's appointment", "date": "2100-01-01"}`),
|
|
21
|
+
{
|
|
22
|
+
note: "Bicentennial doctor's appointment",
|
|
23
|
+
date: Date.fromString("2100-01-01"),
|
|
24
|
+
extra: None,
|
|
25
|
+
},
|
|
26
|
+
),
|
|
27
|
+
(
|
|
28
|
+
%raw(`{
|
|
29
|
+
"note": "Bicentennial doctor's appointment",
|
|
30
|
+
"date": "2100-01-01",
|
|
31
|
+
"extra": "Don't take your stop-aging pills the night before the appointment",
|
|
32
|
+
}`),
|
|
33
|
+
{
|
|
34
|
+
note: "Bicentennial doctor's appointment",
|
|
35
|
+
date: Date.fromString("2100-01-01"),
|
|
36
|
+
extra: Some("Don't take your stop-aging pills the night before the appointment"),
|
|
37
|
+
},
|
|
38
|
+
),
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
test("Correctly deserializes data", qunit => {
|
|
42
|
+
qunit->expect(valid->Array.length)
|
|
43
|
+
valid->Array.forEach(
|
|
44
|
+
((data, expected)) => {
|
|
45
|
+
Console.log2("Running sample", data)
|
|
46
|
+
switch Appointment.Deserializer.fromJSON(data) {
|
|
47
|
+
| Ok(result) => qunit->deepEqual(result, expected, "result == expected")
|
|
48
|
+
| Error(msg) => Console.error(msg)
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
let invalid = [
|
|
55
|
+
(
|
|
56
|
+
"Missing non-optional field",
|
|
57
|
+
%raw(`{"extra": "Bicentennial doctor's appointment", "date": "2100-01-01"}`),
|
|
58
|
+
),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
test("Correctly catches invalid data", qunit => {
|
|
62
|
+
qunit->expect(invalid->Array.length)
|
|
63
|
+
invalid->Array.forEach(
|
|
64
|
+
((message, data)) => {
|
|
65
|
+
Console.log3("Running sample", message, data)
|
|
66
|
+
switch Appointment.Deserializer.fromJSON(data) {
|
|
67
|
+
| Ok(result) => Console.error2("Invalid being accepted: ", result)
|
|
68
|
+
| Error(msg) => {
|
|
69
|
+
Console.log2("Correctly detected:", msg)
|
|
70
|
+
qunit->ok(true, true)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
module_("Recursive deserializer", _ => {
|
|
79
|
+
module TrivialTree = {
|
|
80
|
+
type rec t<'a> = {
|
|
81
|
+
data: 'a,
|
|
82
|
+
children: array<t<'a>>,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module DeserializerImpl = Deser.MakeDeserializer({
|
|
86
|
+
type payload
|
|
87
|
+
type rec t = {
|
|
88
|
+
data: payload,
|
|
89
|
+
children: array<t>,
|
|
90
|
+
}
|
|
91
|
+
open Deser.Field
|
|
92
|
+
|
|
93
|
+
let fields = Object([("data", Any), ("children", Array(Self))])
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
module Deserializer = {
|
|
97
|
+
include DeserializerImpl
|
|
98
|
+
|
|
99
|
+
let fromJSON = data => data->DeserializerImpl.fromJSON->Result.map(x => x->Obj.magic)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let valid: array<(_, TrivialTree.t<string>)> = [
|
|
104
|
+
(
|
|
105
|
+
%raw(`{"data": "A", "children": [{"data": "A1", "children": []}, {"data": "B", "children": [{"data": "C", "children": []}, {"data": "D", "children": []}]}]}`),
|
|
106
|
+
{
|
|
107
|
+
data: "A",
|
|
108
|
+
children: [
|
|
109
|
+
{data: "A1", children: []},
|
|
110
|
+
{data: "B", children: [{data: "C", children: []}, {data: "D", children: []}]},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
),
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
test("Trivial recursion detection: Ok", qunit => {
|
|
117
|
+
qunit->expect(1)
|
|
118
|
+
qunit->deepEqual(TrivialTree.Deserializer.checkFieldsSanity(), Ok(), "Ok")
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test("Infinite list", qunit => {
|
|
122
|
+
module InfiniteList = Deser.MakeDeserializer({
|
|
123
|
+
open Deser.Field
|
|
124
|
+
|
|
125
|
+
type t
|
|
126
|
+
let fields = Object([("head", String), ("tail", Self)])
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
qunit->expect(1)
|
|
130
|
+
qunit->deepEqual(InfiniteList.checkFieldsSanity()->Result.isError, true, "Ok")
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
test("Finite list", qunit => {
|
|
134
|
+
module List = Deser.MakeDeserializer({
|
|
135
|
+
open Deser.Field
|
|
136
|
+
|
|
137
|
+
type t
|
|
138
|
+
let fields = Object([("head", String), ("tail", Optional(Self))])
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
qunit->expect(1)
|
|
142
|
+
qunit->deepEqual(List.checkFieldsSanity(), Ok(), "Ok")
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
test("Correctly deserializes recursive data", qunit => {
|
|
146
|
+
qunit->expect(valid->Array.length)
|
|
147
|
+
valid->Array.forEach(
|
|
148
|
+
((data, expected)) => {
|
|
149
|
+
Console.log2("Running sample", data)
|
|
150
|
+
switch TrivialTree.Deserializer.fromJSON(data) {
|
|
151
|
+
| Ok(result) => qunit->deepEqual(result, expected, "result == expected")
|
|
152
|
+
| Error(msg) => Console.error(msg)
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test("Recursion in sub-deserializer", qunit => {
|
|
159
|
+
module List = Deser.MakeDeserializer({
|
|
160
|
+
open Deser.Field
|
|
161
|
+
|
|
162
|
+
type t
|
|
163
|
+
let fields = Object([("head", String), ("tail", Optional(Self))])
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
module Ledger = Deser.MakeDeserializer({
|
|
167
|
+
open Deser.Field
|
|
168
|
+
|
|
169
|
+
type t
|
|
170
|
+
let fields = Object([("records", Deserializer(module(List))), ("next", Optional(Self))])
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
let data = %raw(`
|
|
174
|
+
{"records": {"head": "A", "tail": {"head": "B"}},
|
|
175
|
+
"next": {"records": {"head": "A", "tail": {"head": "B"}}}}
|
|
176
|
+
`)
|
|
177
|
+
let expected = {
|
|
178
|
+
"records": {"head": "A", "tail": {"head": "B", "tail": None}},
|
|
179
|
+
"next": {
|
|
180
|
+
"records": {"head": "A", "tail": {"head": "B", "tail": None}},
|
|
181
|
+
"next": None,
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
qunit->expect(1)
|
|
186
|
+
qunit->deepEqual(data->Ledger.fromJSON->Obj.magic, Ok(expected), "nice ledger")
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
module_("Type safety limits", _ => {
|
|
191
|
+
test("ill-defined deserializer can cheat the type system", qunit => {
|
|
192
|
+
module X = Deser.MakeDeserializer({
|
|
193
|
+
type t = string
|
|
194
|
+
let fields = Deser.Field.Array(Int)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
let data = %raw("[1]")
|
|
198
|
+
qunit->throws(
|
|
199
|
+
_ => {
|
|
200
|
+
let illString = data->X.fromJSON->Result.getOr("")
|
|
201
|
+
try {
|
|
202
|
+
Console.info3(__MODULE__, "This will fail with a type error", illString->String.charAt(0))
|
|
203
|
+
} catch {
|
|
204
|
+
| e =>
|
|
205
|
+
Console.error(e)
|
|
206
|
+
throw(e)
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"Expected: TypeError: illString.charAt is not a function",
|
|
210
|
+
)
|
|
211
|
+
})
|
|
212
|
+
})
|