@kaiko.io/rescript-deser 6.0.0 → 7.0.0-alpha.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.
package/README.md CHANGED
@@ -50,7 +50,7 @@ The resultant module has type:
50
50
  module type Deserializer = {
51
51
  type t
52
52
  let name: string
53
- let fromJSON: Js.Json.t => result<t, string>
53
+ let fromJSON: RescriptCore.JSON.t => result<t, string>
54
54
  }
55
55
  ```
56
56
 
@@ -80,10 +80,10 @@ module type Serializable = {
80
80
  JS representation of the object that comes from the JSON data.
81
81
 
82
82
  - `Date`, parses either a string representation of a date (datetime) or a
83
- floating point representation of date (datetime) into `Js.Date.t`; we make
83
+ floating point representation of date (datetime) into `Date.t`; we make
84
84
  sure the result is valid and won't return NaN afterwards.
85
85
 
86
- This basically calls, `Js.Date.fromString` or `Js.Date.fromFloat`; and tests
86
+ This basically calls, `Date.fromString` or `Date.fromTime`; and tests
87
87
  the resulting value.
88
88
 
89
89
  `Datetime` is an alias for `Date`.
package/lib/bs/.bsdeps CHANGED
@@ -2,8 +2,8 @@
2
2
  /home/manu/src/kaiko/rescript-deser
3
3
  0
4
4
  0
5
- rescript.json 0x1.9d272e38c4598p+30
6
- tests 0x1.9d272e38c7c35p+30
7
- src/ 0x1.a024aa053ad05p+30
5
+ rescript.json 0x1.a0f2462508cf1p+30
6
+ tests 0x1.a472319d551bdp+30
7
+ src/ 0x1.a4721291940c2p+30
8
8
  ===
9
- /home/manu/src/kaiko/rescript-deser/node_modules/rescript/linux/rescript.exe 0x1.a0f1eb2331822p+30
9
+ /home/manu/src/kaiko/rescript-deser/node_modules/rescript/linux/rescript.exe 0x1.a4720d14765c9p+30
@@ -1,2 +1,2 @@
1
- #Start(1748794171703)
2
- #Done(1748794171704)
1
+ #Start(1763479665497)
2
+ #Done(1763479665497)
package/lib/bs/.ninja_log CHANGED
@@ -1,45 +1,57 @@
1
1
  # ninja log v6
2
- 1 9 1732888969609093921 tests/index.ast 901a254e17150891
3
- 0 9 1732888969605760485 tests/QUnit.ast 24679400da87e6b0
4
- 0 11 1732888969609093921 src/Deser.ast 60777bbe51986b3b
2
+ 1 18 1732888969609093921 tests/QUnit.cmi f57b53b8aff27629
3
+ 82 114 1763477825207250407 ../es6/tests/index.js fe0b84000747f3c8
4
+ 82 114 1763477825207250407 tests/index.cmj fe0b84000747f3c8
5
+ 6 8 1748799957991823370 tests/index.d 44655d60e4c70d26
6
+ 23 82 1763477825165250216 ../es6/src/Deser.js 9301f060e01e8bd6
5
7
  9 11 1732888969609093921 tests/QUnit.d f74946ee3200f9fa
6
- 9 12 1732888969609093921 tests/index.d 44655d60e4c70d26
7
- 11 13 1732888969612427357 src/Deser.d 691c816990b589e8
8
- 11 18 1732888969615760793 tests/QUnit.cmj dfd5051a1aed6ad1
9
- 11 18 1732888969615760793 tests/QUnit.cmi dfd5051a1aed6ad1
10
- 11 18 1732888969615760793 ../es6/tests/QUnit.js dfd5051a1aed6ad1
11
- 11 18 1732888969615760793 ../js/tests/QUnit.js dfd5051a1aed6ad1
12
- 14 36 1732888969635761410 src/Deser.cmj 59c93988df369be8
13
- 14 36 1732888969635761410 src/Deser.cmi 59c93988df369be8
14
- 14 36 1732888969635761410 ../es6/src/Deser.js 59c93988df369be8
15
- 14 36 1732888969635761410 ../js/src/Deser.js 59c93988df369be8
16
- 36 48 1732888969645761719 tests/index.cmj 1492142736e6fe59
17
- 36 48 1732888969645761719 tests/index.cmi 1492142736e6fe59
18
- 36 48 1732888969645761719 ../es6/tests/index.js 1492142736e6fe59
19
- 36 48 1732888969645761719 ../js/tests/index.js 1492142736e6fe59
20
- 1 7 1732889720608155890 tests/index.ast 901a254e17150891
21
- 7 9 1732889720608155890 tests/index.d 44655d60e4c70d26
22
- 1 9 1732889720611489309 src/Deser.ast 60777bbe51986b3b
23
- 9 10 1732889720611489309 src/Deser.d 691c816990b589e8
24
- 10 29 1732889720611489309 src/Deser.cmj 59c93988df369be8
25
- 10 29 1732889720611489309 src/Deser.cmi 59c93988df369be8
26
- 10 29 1732889720611489309 ../es6/src/Deser.js 59c93988df369be8
27
- 10 29 1732889720611489309 ../js/src/Deser.js 59c93988df369be8
28
- 29 43 1732889720608155890 tests/index.cmj 1492142736e6fe59
29
- 29 43 1732889720608155890 tests/index.cmi 1492142736e6fe59
30
- 29 43 1732889720608155890 ../es6/tests/index.js 1492142736e6fe59
31
- 29 43 1732889720608155890 ../js/tests/index.js 1492142736e6fe59
32
- 1 10 1748794057621698671 src/Deser.ast 60777bbe51986b3b
33
- 1 12 1732888969609093921 tests/QUnit.cmj 61e1b54782097a96
34
- 1 12 1732888969609093921 tests/QUnit.cmi 61e1b54782097a96
35
- 1 12 1732888969609093921 ../es6/tests/QUnit.js 61e1b54782097a96
36
- 1 12 1732888969609093921 ../js/tests/QUnit.js 61e1b54782097a96
37
- 10 12 1748794057621698671 src/Deser.d 691c816990b589e8
38
- 12 36 1748794057621698671 src/Deser.cmj 4b6956c2ed469aef
39
- 12 36 1748794057621698671 src/Deser.cmi 4b6956c2ed469aef
40
- 12 36 1748794057621698671 ../es6/src/Deser.js 4b6956c2ed469aef
41
- 12 36 1748794057621698671 ../js/src/Deser.js 4b6956c2ed469aef
42
- 36 52 1732889720608155890 tests/index.cmj 436d06e8f796c935
43
- 36 52 1732889720608155890 tests/index.cmi 436d06e8f796c935
44
- 36 52 1732889720608155890 ../es6/tests/index.js 436d06e8f796c935
45
- 36 52 1732889720608155890 ../js/tests/index.js 436d06e8f796c935
8
+ 1 18 1732888969609093921 ../es6/tests/QUnit.js f57b53b8aff27629
9
+ 23 82 1763477825165250216 src/Deser.cmj 9301f060e01e8bd6
10
+ 1 18 1732888969609093921 ../js/tests/QUnit.js f57b53b8aff27629
11
+ 23 82 1763477825165250216 src/Deser.cmi 9301f060e01e8bd6
12
+ 1 17 1763477825165250216 src/Deser.ast 60777bbe51986b3b
13
+ 82 114 1763477825207250407 ../js/tests/index.js fe0b84000747f3c8
14
+ 82 114 1763477825207250407 tests/index.cmi fe0b84000747f3c8
15
+ 23 82 1763477825165250216 ../js/src/Deser.js 9301f060e01e8bd6
16
+ 1 18 1732888969609093921 tests/QUnit.cmj f57b53b8aff27629
17
+ 18 23 1763477825165250216 src/Deser.d 691c816990b589e8
18
+ 0 9 1732888969605760485 tests/QUnit.ast 24679400da87e6b0
19
+ 0 6 1748799957991823370 tests/index.ast 901a254e17150891
20
+ 0 14 1732888969609093921 tests/QUnit.cmj d4cd0d743e9dbdc2
21
+ 0 14 1732888969609093921 tests/QUnit.cmi d4cd0d743e9dbdc2
22
+ 0 14 1732888969609093921 ../es6/tests/QUnit.js d4cd0d743e9dbdc2
23
+ 0 14 1732888969609093921 ../js/tests/QUnit.js d4cd0d743e9dbdc2
24
+ 1 44 1763477825165250216 src/Deser.cmj d1ae51e3ae7660c0
25
+ 1 44 1763477825165250216 src/Deser.cmi d1ae51e3ae7660c0
26
+ 1 44 1763477825165250216 ../es6/src/Deser.js d1ae51e3ae7660c0
27
+ 1 44 1763477825165250216 ../js/src/Deser.js d1ae51e3ae7660c0
28
+ 44 66 1763477825207250407 tests/index.cmj d34384b01ad99c82
29
+ 44 66 1763477825207250407 tests/index.cmi d34384b01ad99c82
30
+ 44 66 1763477825207250407 ../es6/tests/index.js d34384b01ad99c82
31
+ 44 66 1763477825207250407 ../js/tests/index.js d34384b01ad99c82
32
+ 0 14 1763477953358547914 tests/index.ast 901a254e17150891
33
+ 14 18 1763477953358547914 tests/index.d 44655d60e4c70d26
34
+ 0 19 1732888969609093921 tests/QUnit.cmj eee74ca8670f321f
35
+ 0 19 1732888969609093921 tests/QUnit.cmi eee74ca8670f321f
36
+ 0 19 1732888969609093921 ../es6/tests/QUnit.js eee74ca8670f321f
37
+ 0 19 1732888969609093921 ../js/tests/QUnit.js eee74ca8670f321f
38
+ 0 55 1763477825165250216 src/Deser.cmj 53951e70cb585c7f
39
+ 0 55 1763477825165250216 src/Deser.cmi 53951e70cb585c7f
40
+ 0 55 1763477825165250216 ../es6/src/Deser.js 53951e70cb585c7f
41
+ 0 55 1763477825165250216 ../js/src/Deser.js 53951e70cb585c7f
42
+ 55 82 1763477953358547914 tests/index.cmj 3635592f4bb018fd
43
+ 55 82 1763477953358547914 tests/index.cmi 3635592f4bb018fd
44
+ 55 82 1763477953358547914 ../es6/tests/index.js 3635592f4bb018fd
45
+ 55 82 1763477953358547914 ../js/tests/index.js 3635592f4bb018fd
46
+ 0 15 1732888969609093921 tests/QUnit.cmj 808e7abe2ed23b68
47
+ 0 15 1732888969609093921 tests/QUnit.cmi 808e7abe2ed23b68
48
+ 0 15 1732888969609093921 ../es6/tests/QUnit.js 808e7abe2ed23b68
49
+ 0 15 1732888969609093921 ../js/tests/QUnit.js 808e7abe2ed23b68
50
+ 0 43 1763477825165250216 src/Deser.cmj d15a15aaf540f200
51
+ 0 43 1763477825165250216 src/Deser.cmi d15a15aaf540f200
52
+ 0 43 1763477825165250216 ../es6/src/Deser.js d15a15aaf540f200
53
+ 0 43 1763477825165250216 ../js/src/Deser.js d15a15aaf540f200
54
+ 43 65 1763477953358547914 tests/index.cmj 6ccb61e0c25bd9c7
55
+ 43 65 1763477953358547914 tests/index.cmi 6ccb61e0c25bd9c7
56
+ 43 65 1763477953358547914 ../es6/tests/index.js 6ccb61e0c25bd9c7
57
+ 43 65 1763477953358547914 ../js/tests/index.js 6ccb61e0c25bd9c7
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,351 @@
1
+ open RescriptCore
2
+
3
+ module FieldValue = {
4
+ type t
5
+ external string: string => t = "%identity"
6
+ external int: int => t = "%identity"
7
+ external float: float => t = "%identity"
8
+ external boolean: bool => t = "%identity"
9
+ external array: array<t> => t = "%identity"
10
+ external object: Dict.t<t> => t = "%identity"
11
+ external mapping: Dict.t<t> => t = "%identity"
12
+ external any: 'a => t = "%identity"
13
+ @val external null: t = "undefined"
14
+
15
+ external asString: t => string = "%identity"
16
+ external asInt: t => int = "%identity"
17
+ external asFloat: t => float = "%identity"
18
+ external asBoolean: t => bool = "%identity"
19
+ external asArray: t => array<'a> = "%identity"
20
+ external asObject: t => 'a = "%identity"
21
+ }
22
+
23
+ exception TypeError(string)
24
+
25
+ @doc("The module type of a built deserializer which is suitable to add as a subparser.")
26
+ module type Deserializer = {
27
+ type t
28
+ let name: string
29
+ let fromJSON: JSON.t => result<t, string>
30
+ let checkFieldsSanity: unit => result<unit, string>
31
+ }
32
+
33
+ module Field = {
34
+ type rec t =
35
+ | Any
36
+ | String
37
+ | Literal(string)
38
+ | Int
39
+ | Float
40
+ | Boolean
41
+ | Array(t)
42
+ // These SHOULD strings in ISO format, but we only validate the string
43
+ // can be represented in Js.Date without spewing NaN all over the place;
44
+ // Js.Date.fromString("xxx") returns an object that is mostly unusable.
45
+ ///
46
+ // We also allow floats and then use Date.fromTime
47
+ | Date
48
+ | Datetime // alias of Date
49
+
50
+ | Tuple(array<t>)
51
+ | Object(array<(string, t)>)
52
+ | Optional(t)
53
+ | OptionalWithDefault(t, FieldValue.t)
54
+ // An arbitrary mapping from names to other arbitrary fields. The
55
+ // difference with Object, is that you don't know the names of the
56
+ // expected entries.
57
+ | Mapping(t)
58
+
59
+ | Deserializer(module(Deserializer))
60
+
61
+ // A specialized Array of deserialized items that ignores unparsable
62
+ // items and returns the valid collection. This saves the user from
63
+ // writing 'Array(DefaultWhenInvalid(Optional(Deserializer(module(M)))))'
64
+ // and then post-process the list of items with 'Array.keepSome'
65
+ | Collection(module(Deserializer))
66
+ | DefaultWhenInvalid(t, FieldValue.t)
67
+
68
+ // FIXME: this is used to add additional restrictions like variadictInt or
69
+ // variadicString; but I find it too type-unsafe. I might consider having
70
+ // a Constraints for this in the future.
71
+ | Morphism(t, FieldValue.t => FieldValue.t)
72
+
73
+ | Self
74
+
75
+ let usingString = (f: string => 'a) => value => value->FieldValue.asString->f->FieldValue.any
76
+ let usingInt = (f: int => 'a) => value => value->FieldValue.asInt->f->FieldValue.any
77
+ let usingFloat = (f: float => 'a) => value => value->FieldValue.asFloat->f->FieldValue.any
78
+ let usingBoolean = (f: bool => 'a) => value => value->FieldValue.asBoolean->f->FieldValue.any
79
+ let usingArray = (f: array<'a> => 'b) => value => value->FieldValue.asArray->f->FieldValue.any
80
+ let usingObject = (f: 'a => 'b) => value => value->FieldValue.asObject->f->FieldValue.any
81
+
82
+ let variadicInt = (hint: string, fromJs: int => option<'variadicType>) => Morphism(
83
+ Int,
84
+ usingInt(i => {
85
+ switch i->fromJs {
86
+ | Some(internalValue) => internalValue
87
+ | None =>
88
+ raise(TypeError(`This Int(${i->Int.toString}) not a valid value here. Hint: ${hint}`))
89
+ }
90
+ }),
91
+ )
92
+ let variadicString = (hint: string, fromJs: string => option<'variadicType>) => Morphism(
93
+ String,
94
+ usingString(i => {
95
+ switch i->fromJs {
96
+ | Some(internalValue) => internalValue
97
+ | None => raise(TypeError(`This String("${i}") not a valid value here. Hint: ${hint}`))
98
+ }
99
+ }),
100
+ )
101
+
102
+ let rec toString = (type_: t) =>
103
+ switch type_ {
104
+ | Any => "Any"
105
+ | String => "String"
106
+ | Literal(lit) => `Literal: ${lit}`
107
+ | Int => "Integer"
108
+ | Float => "Float"
109
+ | Boolean => "Boolean"
110
+ | Datetime
111
+ | Date => "Date"
112
+ | Self => "Self (recursive)"
113
+ | Collection(m) => {
114
+ module M = unpack(m: Deserializer)
115
+ "Collection of " ++ M.name
116
+ }
117
+
118
+ | Array(t) => "Array of " ++ t->toString
119
+ | Tuple(bases) => `Tuple of (${bases->Array.map(toString)->Array.join(", ")})`
120
+ | Object(fields) => {
121
+ let desc = fields->Array.map(((field, t)) => `${field}: ${t->toString}`)->Array.join(", ")
122
+ `Object of {${desc}}`
123
+ }
124
+
125
+ | OptionalWithDefault(t, _)
126
+ | Optional(t) =>
127
+ "Null of " ++ t->toString
128
+ | Mapping(t) => `Mapping of ${t->toString}`
129
+ | Morphism(t, _) => t->toString ++ " to apply a morphism"
130
+ | Deserializer(m) => {
131
+ module M = unpack(m: Deserializer)
132
+ M.name
133
+ }
134
+
135
+ | DefaultWhenInvalid(t, _) => `Protected ${t->toString}`
136
+ }
137
+
138
+ let _taggedToString = (tagged: JSON.t) => {
139
+ switch tagged {
140
+ | Boolean(false) => "Boolean(false)"
141
+ | Boolean(true) => "Boolean(true)"
142
+ | Null => "Null"
143
+ | String(text) => `String("${text}")`
144
+ | Number(number) => `Number(${number->Float.toString})`
145
+ | Object(obj) => `Object(${obj->JSON.stringifyAny->Option.getOr("...")})`
146
+ | Array(array) => `Array(${array->JSON.stringifyAny->Option.getOr("...")})`
147
+ }
148
+ }
149
+
150
+ let rec extractValue = (
151
+ values: Dict.t<JSON.t>,
152
+ field: string,
153
+ shape: t,
154
+ self: t,
155
+ ): FieldValue.t => {
156
+ switch values->Dict.get(field) {
157
+ | Some(value) => value->fromUntagged(shape, self)
158
+ | None =>
159
+ switch shape {
160
+ | DefaultWhenInvalid(_, _) => JSON.Null->fromUntagged(shape, self)
161
+ | Optional(_) => JSON.Null->fromUntagged(shape, self)
162
+ | OptionalWithDefault(_, default) => default
163
+ | _ => raise(TypeError(`Missing non-optional field '${field}'`))
164
+ }
165
+ }
166
+ }
167
+ and fromUntagged: (JSON.t, t, t) => FieldValue.t = (untagged, shape, self) => {
168
+ switch (shape, untagged) {
169
+ | (Any, _) => untagged->FieldValue.any
170
+
171
+ | (Literal(expected), String(text)) if text == expected => FieldValue.string(text)
172
+ | (Literal(expected), String(text)) =>
173
+ raise(TypeError(`Expecting literal ${expected}, got ${text}`))
174
+
175
+ | (String, String(text)) => FieldValue.string(text)
176
+
177
+ | (Int, Number(number)) => FieldValue.int(number->Float.toInt)
178
+
179
+ | (Float, Number(number)) => FieldValue.float(number)
180
+
181
+ | (Boolean, Boolean(v)) => FieldValue.boolean(v)
182
+
183
+ | (Tuple(bases), Array(items)) => {
184
+ let lenbases = bases->Array.length
185
+ let lenitems = items->Array.length
186
+ if lenbases == lenitems {
187
+ let values = Belt.Array.zipBy(items, bases, (i, b) => fromUntagged(i, b, self))
188
+ values->FieldValue.array
189
+ } else {
190
+ raise(
191
+ TypeError(`Expecting ${lenbases->Int.toString} items, got ${lenitems->Int.toString}`),
192
+ )
193
+ }
194
+ }
195
+
196
+ | (Datetime | Date, String(s)) =>
197
+ let r = Date.fromString(s)
198
+ if r->Date.getDate->Int.toFloat->Float.isNaN {
199
+ raise(TypeError(`Invalid date ${s}`))
200
+ }
201
+ r->FieldValue.any
202
+
203
+ | (Datetime | Date, Number(f)) =>
204
+ let r = Date.fromTime(f)
205
+ if r->Date.getDate->Int.toFloat->Float.isNaN {
206
+ raise(TypeError(`Invalid date ${f->Float.toString}`))
207
+ }
208
+ r->FieldValue.any
209
+
210
+ | (Array(shape), Array(items)) =>
211
+ FieldValue.array(items->Array.map(item => item->fromUntagged(shape, self)))
212
+
213
+ | (Mapping(f), Object(values)) =>
214
+ values->Dict.mapValues(v => v->fromUntagged(f, self))->FieldValue.mapping
215
+
216
+ | (Object(fields), Object(values)) =>
217
+ fields
218
+ ->Array.map(((field, shape)) => {
219
+ let value = switch extractValue(values, field, shape, self) {
220
+ | value => value
221
+ | exception TypeError(msg) => raise(TypeError(`Field "${field}": ${msg}`))
222
+ }
223
+ (field, value)
224
+ })
225
+ ->Dict.fromArray
226
+ ->FieldValue.object
227
+
228
+ | (OptionalWithDefault(_, value), Null) => value
229
+ | (OptionalWithDefault(shape, _), _) => untagged->fromUntagged(shape, self)
230
+ | (Optional(_), Null) => FieldValue.null
231
+ | (Optional(shape), _) => untagged->fromUntagged(shape, self)
232
+ | (Morphism(shape, f), _) => untagged->fromUntagged(shape, self)->f->FieldValue.any
233
+
234
+ | (Collection(m), Array(items)) => {
235
+ module M = unpack(m: Deserializer)
236
+ items
237
+ ->Array.map(M.fromJSON)
238
+ ->Array.filterMap(x =>
239
+ switch x {
240
+ | Ok(v) => Some(v)
241
+ | Error(msg) =>
242
+ Console.warn3(__MODULE__, "Could not deserialize value in the collection", msg)
243
+ None
244
+ }
245
+ )
246
+ ->Array.map(FieldValue.any)
247
+ ->FieldValue.array
248
+ }
249
+
250
+ | (Deserializer(m), _) => {
251
+ module M = unpack(m: Deserializer)
252
+ switch untagged->M.fromJSON {
253
+ | Ok(res) => res->FieldValue.any
254
+ | Error(msg) => raise(TypeError(msg))
255
+ }
256
+ }
257
+
258
+ | (DefaultWhenInvalid(t, default), _) =>
259
+ switch untagged->fromUntagged(t, self) {
260
+ | res => res
261
+ | exception TypeError(msg) => {
262
+ Console.warn2("Detected and ignore (with default): ", msg)
263
+ default
264
+ }
265
+ }
266
+ | (Self, _) => untagged->fromUntagged(self, self)
267
+ | (expected, actual) =>
268
+ raise(TypeError(`Expected ${expected->toString}, but got ${actual->_taggedToString} instead`))
269
+ }
270
+ }
271
+
272
+ let rec checkFieldsSanity = (name: string, fields: t, optional: bool): result<unit, string> =>
273
+ switch (fields, optional) {
274
+ | (Self, false) => Error(`${name}: Trivial infinite recursion 'let fields = Self'`)
275
+ | (Self, true) => Ok()
276
+
277
+ | (Any, _) => Ok()
278
+ | (String, _) | (Float, _) | (Int, _) | (Literal(_), _) => Ok()
279
+ | (Boolean, _) | (Date, _) | (Datetime, _) => Ok()
280
+ | (Morphism(_, _), _) => Ok()
281
+
282
+ | (Collection(mod), _)
283
+ | (Deserializer(mod), _) => {
284
+ module M = unpack(mod: Deserializer)
285
+ switch M.checkFieldsSanity() {
286
+ | Ok() => Ok()
287
+ | Error(msg) => Error(`${name}/ ${msg}`)
288
+ }
289
+ }
290
+
291
+ | (DefaultWhenInvalid(fields, _), _)
292
+ | (OptionalWithDefault(fields, _), _)
293
+ | (Optional(fields), _) =>
294
+ checkFieldsSanity(name, fields, true)
295
+
296
+ | (Object(fields), optional) =>
297
+ fields
298
+ ->Array.map(((fieldName, field)) => () =>
299
+ checkFieldsSanity(`${name}::${fieldName}`, field, optional))
300
+ ->Array.reduce(Ok([]), (res, nextitem) =>
301
+ res->Result.flatMap(arr => nextitem()->Result.map(i => arr->Array.concat([i])))
302
+ )
303
+ ->Result.map(_ => ())
304
+
305
+ // Mappings and arrays can be empty, so their payloads are
306
+ // automatically optional.
307
+ | (Mapping(field), _) | (Array(field), _) => checkFieldsSanity(name, field, true)
308
+
309
+ | (Tuple(fields), optional) =>
310
+ fields
311
+ ->Array.mapWithIndex((field, index) => () =>
312
+ checkFieldsSanity(`${name}[${index->Int.toString}]`, field, optional))
313
+ ->Array.reduce(Ok([]), (res, nextitem) =>
314
+ res->Result.flatMap(arr => nextitem()->Result.map(i => arr->Array.concat([i])))
315
+ )
316
+ ->Result.map(_ => ())
317
+ }
318
+ }
319
+
320
+ module type Serializable = {
321
+ type t
322
+ let fields: Field.t
323
+ }
324
+
325
+ module MakeDeserializer = (S: Serializable): (Deserializer with type t = S.t) => {
326
+ type t = S.t
327
+ let fields = S.fields
328
+ %%private(let (loc, _f) = __LOC_OF__(module(S: Serializable)))
329
+ let name = `Deserializer ${__MODULE__}, ${loc}`
330
+
331
+ %%private(external _toNativeType: FieldValue.t => t = "%identity")
332
+
333
+ @doc("Checks for trivial infinite-recursion in the fields of the module.
334
+
335
+ Notice this algorithm is just an heuristic, and it might happen that are
336
+ cases of infinite-recursion not detected and cases where detection is a
337
+ false positive.
338
+
339
+ You should use this only while debugging/developing to verify your data.
340
+
341
+ ")
342
+ let checkFieldsSanity = () => Field.checkFieldsSanity(name, fields, false)
343
+
344
+ @doc("Parse a `'a` into `result<t, string>`")
345
+ let fromJSON: JSON.t => result<t, _> = json => {
346
+ switch json->Field.fromUntagged(fields, fields) {
347
+ | res => Ok(res->_toNativeType)
348
+ | exception TypeError(e) => Error(e)
349
+ }
350
+ }
351
+ }
Binary file
Binary file
Binary file