@greenlabs/ppx-spice 0.1.6 → 0.1.7-rc2
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/.github/workflows/build_linux.yml +11 -29
- package/.github/workflows/build_macos.yml +10 -30
- package/.github/workflows/build_windows.yml +10 -24
- package/.github/workflows/publish.yml +21 -59
- package/.github/workflows/publish_prerelease.yml +24 -62
- package/.vscode/settings.json +6 -0
- package/CHANGELOG.md +7 -0
- package/README.md +14 -39
- package/package.json +2 -2
- package/ppx-linux.exe +0 -0
- package/ppx-osx.exe +0 -0
- package/src/ppx/codecs.ml +79 -141
- package/src/ppx/polyvariants.ml +6 -9
- package/src/ppx/records.ml +32 -11
- package/src/ppx/utils.ml +3 -1
- package/src/ppx/variants.ml +5 -9
- package/src/ppx_spice.opam +21 -0
- package/src/rescript/Spice.res +6 -1
- package/test/__tests__/test_polyvariants.js +17 -17
- package/test/__tests__/test_records.js +65 -14
- package/test/__tests__/test_records.res +60 -0
- package/test/__tests__/test_variants.js +28 -28
- package/test/package.json +1 -1
- package/test/src/Polyvariants.js +2 -1
- package/test/src/Records.js +83 -20
- package/test/src/Records.res +6 -0
- package/test/src/Records.resi +6 -0
- package/test/src/Spice.js +20 -7
- package/test/src/Variants.js +2 -1
- package/test/yarn.lock +4 -4
- package/src/esy.json +0 -21
- package/src/ppx_spice.install +0 -43
package/src/ppx/codecs.ml
CHANGED
|
@@ -27,165 +27,103 @@ and generate_constr_codecs { do_encode; do_decode }
|
|
|
27
27
|
{ Location.txt = identifier; loc } =
|
|
28
28
|
let open Longident in
|
|
29
29
|
match identifier with
|
|
30
|
-
| Lident "string" ->
|
|
31
|
-
(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
| Lident "
|
|
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
|
-
match do_decode with
|
|
77
|
-
| true -> Some [%expr Spice.arrayFromJson]
|
|
78
|
-
| false -> None ))
|
|
79
|
-
| Lident "list" -> (
|
|
80
|
-
( (match do_encode with
|
|
81
|
-
| true -> Some [%expr Spice.listToJson]
|
|
82
|
-
| false -> None),
|
|
83
|
-
match do_decode with
|
|
84
|
-
| true -> Some [%expr Spice.listFromJson]
|
|
85
|
-
| false -> None ))
|
|
86
|
-
| Lident "option" -> (
|
|
87
|
-
( (match do_encode with
|
|
88
|
-
| true -> Some [%expr Spice.optionToJson]
|
|
89
|
-
| false -> None),
|
|
90
|
-
match do_decode with
|
|
91
|
-
| true -> Some [%expr Spice.optionFromJson]
|
|
92
|
-
| false -> None ))
|
|
93
|
-
| Ldot (Ldot (Lident "Belt", "Result"), "t") -> (
|
|
94
|
-
( (match do_encode with
|
|
95
|
-
| true -> Some [%expr Spice.resultToJson]
|
|
96
|
-
| false -> None),
|
|
97
|
-
match do_decode with
|
|
98
|
-
| true -> Some [%expr Spice.resultFromJson]
|
|
99
|
-
| false -> None ))
|
|
100
|
-
| Ldot (Ldot (Lident "Js", "Dict"), "t") -> (
|
|
101
|
-
( (match do_encode with
|
|
102
|
-
| true -> Some [%expr Spice.dictToJson]
|
|
103
|
-
| false -> None),
|
|
104
|
-
match do_decode with
|
|
105
|
-
| true -> Some [%expr Spice.dictFromJson]
|
|
106
|
-
| false -> None ))
|
|
107
|
-
| Ldot (Ldot (Lident "Js", "Json"), "t") -> (
|
|
108
|
-
( (match do_encode with true -> Some [%expr fun v -> v] | false -> None),
|
|
109
|
-
match do_decode with
|
|
110
|
-
| true -> Some [%expr fun v -> Belt.Result.Ok v]
|
|
111
|
-
| false -> None ))
|
|
112
|
-
| Lident s -> (
|
|
113
|
-
( (match do_encode with
|
|
114
|
-
| true -> Some (make_ident_expr (s ^ Utils.encoder_func_suffix))
|
|
115
|
-
| false -> None),
|
|
116
|
-
match do_decode with
|
|
117
|
-
| true -> Some (make_ident_expr (s ^ Utils.decoder_func_suffix))
|
|
118
|
-
| false -> None ))
|
|
119
|
-
| Ldot (left, right) -> (
|
|
120
|
-
( (match do_encode with
|
|
121
|
-
| true ->
|
|
122
|
-
Some
|
|
123
|
-
(Exp.ident
|
|
124
|
-
(mknoloc (Ldot (left, right ^ Utils.encoder_func_suffix))))
|
|
125
|
-
| false -> None),
|
|
126
|
-
match do_decode with
|
|
127
|
-
| true ->
|
|
128
|
-
Some
|
|
129
|
-
(Exp.ident
|
|
130
|
-
(mknoloc (Ldot (left, right ^ Utils.decoder_func_suffix))))
|
|
131
|
-
| false -> None ))
|
|
30
|
+
| Lident "string" ->
|
|
31
|
+
( some_if_true do_encode [%expr Spice.stringToJson],
|
|
32
|
+
some_if_true do_decode [%expr Spice.stringFromJson] )
|
|
33
|
+
| Lident "int" ->
|
|
34
|
+
( some_if_true do_encode [%expr Spice.intToJson],
|
|
35
|
+
some_if_true do_decode [%expr Spice.intFromJson] )
|
|
36
|
+
| Lident "int64" ->
|
|
37
|
+
( some_if_true do_encode [%expr Spice.int64ToJson],
|
|
38
|
+
some_if_true do_decode [%expr Spice.int64FromJson] )
|
|
39
|
+
| Lident "float" ->
|
|
40
|
+
( some_if_true do_encode [%expr Spice.floatToJson],
|
|
41
|
+
some_if_true do_decode [%expr Spice.floatFromJson] )
|
|
42
|
+
| Lident "bool" ->
|
|
43
|
+
( some_if_true do_encode [%expr Spice.boolToJson],
|
|
44
|
+
some_if_true do_decode [%expr Spice.boolFromJson] )
|
|
45
|
+
| Lident "unit" ->
|
|
46
|
+
( some_if_true do_encode [%expr Spice.unitToJson],
|
|
47
|
+
some_if_true do_decode [%expr Spice.unitFromJson] )
|
|
48
|
+
| Lident "array" ->
|
|
49
|
+
( some_if_true do_encode [%expr Spice.arrayToJson],
|
|
50
|
+
some_if_true do_decode [%expr Spice.arrayFromJson] )
|
|
51
|
+
| Lident "list" ->
|
|
52
|
+
( some_if_true do_encode [%expr Spice.listToJson],
|
|
53
|
+
some_if_true do_decode [%expr Spice.listFromJson] )
|
|
54
|
+
| Lident "option" ->
|
|
55
|
+
( some_if_true do_encode [%expr Spice.optionToJson],
|
|
56
|
+
some_if_true do_decode [%expr Spice.optionFromJson] )
|
|
57
|
+
| Ldot (Ldot (Lident "Belt", "Result"), "t") ->
|
|
58
|
+
( some_if_true do_encode [%expr Spice.resultToJson],
|
|
59
|
+
some_if_true do_decode [%expr Spice.resultFromJson] )
|
|
60
|
+
| Ldot (Ldot (Lident "Js", "Dict"), "t") ->
|
|
61
|
+
( some_if_true do_encode [%expr Spice.dictToJson],
|
|
62
|
+
some_if_true do_decode [%expr Spice.dictFromJson] )
|
|
63
|
+
| Ldot (Ldot (Lident "Js", "Json"), "t") ->
|
|
64
|
+
( some_if_true do_encode [%expr fun v -> v],
|
|
65
|
+
some_if_true do_decode [%expr fun v -> Belt.Result.Ok v] )
|
|
66
|
+
| Lident s ->
|
|
67
|
+
( some_if_true do_encode (make_ident_expr (s ^ Utils.encoder_func_suffix)),
|
|
68
|
+
some_if_true do_decode (make_ident_expr (s ^ Utils.decoder_func_suffix))
|
|
69
|
+
)
|
|
70
|
+
| Ldot (left, right) ->
|
|
71
|
+
( some_if_true do_encode
|
|
72
|
+
(Exp.ident (mknoloc (Ldot (left, right ^ Utils.encoder_func_suffix)))),
|
|
73
|
+
some_if_true do_decode
|
|
74
|
+
(Exp.ident (mknoloc (Ldot (left, right ^ Utils.decoder_func_suffix))))
|
|
75
|
+
)
|
|
132
76
|
| Lapply (_, _) -> fail loc "Lapply syntax not yet handled by spice"
|
|
133
77
|
|
|
134
|
-
and generate_codecs (
|
|
78
|
+
and generate_codecs ?(is_optional = false)
|
|
79
|
+
({ do_encode; do_decode } as generator_settings)
|
|
135
80
|
{ ptyp_desc; ptyp_loc; ptyp_attributes } =
|
|
136
81
|
match ptyp_desc with
|
|
137
82
|
| Ptyp_any -> fail ptyp_loc "Can't generate codecs for `any` type"
|
|
138
83
|
| Ptyp_arrow (_, _, _) ->
|
|
139
84
|
fail ptyp_loc "Can't generate codecs for function type"
|
|
140
85
|
| Ptyp_package _ -> fail ptyp_loc "Can't generate codecs for module type"
|
|
141
|
-
| Ptyp_tuple types ->
|
|
86
|
+
| Ptyp_tuple types ->
|
|
142
87
|
let composite_codecs =
|
|
143
|
-
List.map (generate_codecs generator_settings) types
|
|
88
|
+
List.map (generate_codecs ~is_optional generator_settings) types
|
|
144
89
|
in
|
|
145
|
-
(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|> List.map (fun (_, d) -> Option.get d)
|
|
157
|
-
|> Tuple.generate_decoder)
|
|
158
|
-
| false -> None ))
|
|
159
|
-
| Ptyp_var s -> (
|
|
160
|
-
( (match do_encode with
|
|
161
|
-
| true -> Some (make_ident_expr (encoder_var_prefix ^ s))
|
|
162
|
-
| false -> None),
|
|
163
|
-
match do_decode with
|
|
164
|
-
| true -> Some (make_ident_expr (decoder_var_prefix ^ s))
|
|
165
|
-
| false -> None ))
|
|
90
|
+
( some_if_true do_encode
|
|
91
|
+
(composite_codecs
|
|
92
|
+
|> List.map (fun (e, _) -> Option.get e)
|
|
93
|
+
|> Tuple.generate_encoder),
|
|
94
|
+
some_if_true do_decode
|
|
95
|
+
(composite_codecs
|
|
96
|
+
|> List.map (fun (_, d) -> Option.get d)
|
|
97
|
+
|> Tuple.generate_decoder) )
|
|
98
|
+
| Ptyp_var s ->
|
|
99
|
+
( some_if_true do_encode (make_ident_expr (encoder_var_prefix ^ s)),
|
|
100
|
+
some_if_true do_decode (make_ident_expr (decoder_var_prefix ^ s)) )
|
|
166
101
|
| Ptyp_constr (constr, typeArgs) -> (
|
|
167
102
|
let custom_codec = get_attribute_by_name ptyp_attributes "spice.codec" in
|
|
168
103
|
let encode, decode =
|
|
169
104
|
match custom_codec with
|
|
170
105
|
| Ok None -> generate_constr_codecs generator_settings constr
|
|
171
|
-
| Ok (Some attribute) ->
|
|
106
|
+
| Ok (Some attribute) ->
|
|
172
107
|
let expr = get_expression_from_payload attribute in
|
|
173
|
-
(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
| true ->
|
|
182
|
-
Some
|
|
183
|
-
[%expr
|
|
184
|
-
let _, d = [%e expr] in
|
|
185
|
-
d]
|
|
186
|
-
| false -> None ))
|
|
108
|
+
( some_if_true do_encode
|
|
109
|
+
[%expr
|
|
110
|
+
let e, _ = [%e expr] in
|
|
111
|
+
e],
|
|
112
|
+
some_if_true do_decode
|
|
113
|
+
[%expr
|
|
114
|
+
let _, d = [%e expr] in
|
|
115
|
+
d] )
|
|
187
116
|
| Error s -> fail ptyp_loc s
|
|
188
117
|
in
|
|
118
|
+
let encode, decode =
|
|
119
|
+
if is_optional then
|
|
120
|
+
match (encode, decode) with
|
|
121
|
+
| Some encode, Some decode ->
|
|
122
|
+
( Some [%expr Spice.optionToJson [%e encode]],
|
|
123
|
+
Some [%expr Spice.optionFromJson [%e decode]] )
|
|
124
|
+
| _ -> (encode, decode)
|
|
125
|
+
else (encode, decode)
|
|
126
|
+
in
|
|
189
127
|
match List.length typeArgs = 0 with
|
|
190
128
|
| true -> (encode, decode)
|
|
191
129
|
| false -> parameterize_codecs typeArgs encode decode generator_settings)
|
package/src/ppx/polyvariants.ml
CHANGED
|
@@ -219,15 +219,12 @@ let generate_codecs ({ do_encode; do_decode } as generator_settings) row_fields
|
|
|
219
219
|
in
|
|
220
220
|
|
|
221
221
|
let encoder =
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|> Exp.fun_ Asttypes.Nolabel None [%pat? v]
|
|
229
|
-
|> Option.some
|
|
230
|
-
| false -> None
|
|
222
|
+
some_if_true do_encode
|
|
223
|
+
(List.map
|
|
224
|
+
(generate_encoder_case generator_settings unboxed has_attr_as)
|
|
225
|
+
parsed_fields
|
|
226
|
+
|> Exp.match_ [%expr v]
|
|
227
|
+
|> Exp.fun_ Asttypes.Nolabel None [%pat? v])
|
|
231
228
|
in
|
|
232
229
|
|
|
233
230
|
let decoder =
|
package/src/ppx/records.ml
CHANGED
|
@@ -11,8 +11,16 @@ type parsed_decl = {
|
|
|
11
11
|
field : expression;
|
|
12
12
|
codecs : expression option * expression option;
|
|
13
13
|
default : expression option;
|
|
14
|
+
is_optional : bool;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
let optional_attr : Ppxlib.Parsetree.attribute =
|
|
18
|
+
{
|
|
19
|
+
attr_name = { txt = "ns.optional"; loc = Location.none };
|
|
20
|
+
attr_payload = PStr [];
|
|
21
|
+
attr_loc = Location.none;
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
let generate_encoder decls unboxed =
|
|
17
25
|
match unboxed with
|
|
18
26
|
| true ->
|
|
@@ -22,11 +30,17 @@ let generate_encoder decls unboxed =
|
|
|
22
30
|
| false ->
|
|
23
31
|
let arrExpr =
|
|
24
32
|
decls
|
|
25
|
-
|> List.map (fun { key; field; codecs = encoder, _ } ->
|
|
26
|
-
|
|
33
|
+
|> List.map (fun { key; field; codecs = encoder, _; is_optional } ->
|
|
34
|
+
let is_optional =
|
|
35
|
+
if is_optional then [%expr true] else [%expr false]
|
|
36
|
+
in
|
|
37
|
+
[%expr
|
|
38
|
+
[%e key], [%e is_optional], [%e Option.get encoder] [%e field]])
|
|
27
39
|
|> Exp.array
|
|
28
40
|
in
|
|
29
|
-
[%expr
|
|
41
|
+
[%expr
|
|
42
|
+
[%e arrExpr] |> Spice.filterOptional |> Js.Dict.fromArray
|
|
43
|
+
|> Js.Json.object_]
|
|
30
44
|
|> Exp.fun_ Asttypes.Nolabel None [%pat? v]
|
|
31
45
|
|
|
32
46
|
let generate_dict_get { key; codecs = _, decoder; default } =
|
|
@@ -53,7 +67,10 @@ let generate_error_case { key } =
|
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
let generate_final_record_expr decls =
|
|
56
|
-
decls
|
|
70
|
+
decls
|
|
71
|
+
|> List.map (fun { name; is_optional } ->
|
|
72
|
+
let attrs = if is_optional then [ optional_attr ] else [] in
|
|
73
|
+
(lid name, make_ident_expr ~attrs name))
|
|
57
74
|
|> fun l -> [%expr Belt.Result.Ok [%e Exp.record l None]]
|
|
58
75
|
|
|
59
76
|
let generate_success_case { name } success_expr =
|
|
@@ -113,20 +130,24 @@ let parse_decl generator_settings
|
|
|
113
130
|
| Ok None -> Exp.constant (Pconst_string (txt, Location.none, None))
|
|
114
131
|
| Error s -> fail pld_loc s
|
|
115
132
|
in
|
|
133
|
+
let optional_attrs = [ "ns.optional"; "res.optional" ] in
|
|
134
|
+
let is_optional =
|
|
135
|
+
optional_attrs
|
|
136
|
+
|> List.map (fun attr -> get_attribute_by_name pld_attributes attr)
|
|
137
|
+
|> List.exists (function Ok (Some _) -> true | _ -> false)
|
|
138
|
+
in
|
|
139
|
+
|
|
116
140
|
{
|
|
117
141
|
name = txt;
|
|
118
142
|
key;
|
|
119
143
|
field = Exp.field [%expr v] (lid txt);
|
|
120
|
-
codecs = Codecs.generate_codecs generator_settings pld_type;
|
|
144
|
+
codecs = Codecs.generate_codecs ~is_optional generator_settings pld_type;
|
|
121
145
|
default;
|
|
146
|
+
is_optional;
|
|
122
147
|
}
|
|
123
148
|
|
|
124
149
|
let generate_codecs ({ do_encode; do_decode } as generator_settings) decls
|
|
125
150
|
unboxed =
|
|
126
151
|
let parsed_decls = List.map (parse_decl generator_settings) decls in
|
|
127
|
-
(
|
|
128
|
-
|
|
129
|
-
| false -> None),
|
|
130
|
-
match do_decode with
|
|
131
|
-
| true -> Some (generate_decoder parsed_decls unboxed)
|
|
132
|
-
| false -> None )
|
|
152
|
+
( some_if_true do_encode (generate_encoder parsed_decls unboxed),
|
|
153
|
+
some_if_true do_decode (generate_decoder parsed_decls unboxed) )
|
package/src/ppx/utils.ml
CHANGED
|
@@ -24,7 +24,7 @@ let mknoloc txt = mkloc txt Location.none
|
|
|
24
24
|
|
|
25
25
|
let lid ?(loc = Location.none) s = mkloc (Longident.parse s) loc
|
|
26
26
|
|
|
27
|
-
let make_ident_expr s = Exp.ident (mknoloc (longident_parse s))
|
|
27
|
+
let make_ident_expr ?attrs s = Exp.ident ?attrs (mknoloc (longident_parse s))
|
|
28
28
|
|
|
29
29
|
let tuple_or_singleton tuple l =
|
|
30
30
|
match List.length l > 1 with true -> tuple l | false -> List.hd l
|
|
@@ -113,3 +113,5 @@ let attr_warning expr =
|
|
|
113
113
|
attr_payload = PStr [ { pstr_desc = Pstr_eval (expr, []); pstr_loc = loc } ];
|
|
114
114
|
attr_loc = loc;
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
let some_if_true cond a = match cond with true -> Some a | false -> None
|
package/src/ppx/variants.ml
CHANGED
|
@@ -188,15 +188,11 @@ let generate_codecs ({ do_encode; do_decode } as generator_settings)
|
|
|
188
188
|
in
|
|
189
189
|
|
|
190
190
|
let encoder =
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|> Exp.match_ [%expr v]
|
|
197
|
-
|> Exp.fun_ Asttypes.Nolabel None [%pat? v]
|
|
198
|
-
|> Option.some
|
|
199
|
-
| false -> None
|
|
191
|
+
some_if_true do_encode
|
|
192
|
+
(parsed_decls
|
|
193
|
+
|> List.map (generate_encoder_case generator_settings unboxed has_attr_as)
|
|
194
|
+
|> Exp.match_ [%expr v]
|
|
195
|
+
|> Exp.fun_ Asttypes.Nolabel None [%pat? v])
|
|
200
196
|
in
|
|
201
197
|
|
|
202
198
|
let decoder =
|
package/src/ppx_spice.opam
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
opam-version: "2.0"
|
|
2
|
+
name: "ppx_spice"
|
|
3
|
+
version: "0.1.7"
|
|
4
|
+
synopsis: "ReScript PPX which generate JSON (de)serializer"
|
|
5
|
+
description: """
|
|
6
|
+
ReScript PPX which generate JSON (de)serializer
|
|
7
|
+
"""
|
|
8
|
+
maintainer: "Greenlabs Dev <developer@greenlabs.co.kr>"
|
|
9
|
+
authors: "Greenlabs Dev <developer@greenlabs.co.kr>"
|
|
10
|
+
license: "MIT"
|
|
11
|
+
homepage: "https://github.com/green-labs/ppx_spice"
|
|
12
|
+
bug-reports: "https://github.com/green-labs/ppx_spice/issues"
|
|
13
|
+
dev-repo: "git+https://github.com/green-labs/ppx_spice.git"
|
|
14
|
+
depends: [
|
|
15
|
+
"ocaml" { = "4.12.1"}
|
|
16
|
+
"dune" { >= "2.7"}
|
|
17
|
+
"ppxlib" { = "0.23.0"}
|
|
18
|
+
]
|
|
19
|
+
build: [
|
|
20
|
+
["dune" "build" "-p" name "-j" jobs]
|
|
21
|
+
]
|
package/src/rescript/Spice.res
CHANGED
|
@@ -14,7 +14,7 @@ let error = (~path=?, message, value) => {
|
|
|
14
14
|
| None => ""
|
|
15
15
|
| Some(s) => s
|
|
16
16
|
}
|
|
17
|
-
Belt.Result.Error({path
|
|
17
|
+
Belt.Result.Error({path, message, value})
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
let stringToJson = s => Js.Json.string(s)
|
|
@@ -98,6 +98,11 @@ let optionToJson = (encoder, opt) =>
|
|
|
98
98
|
| None => Js.Json.null
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
let filterOptional = arr =>
|
|
102
|
+
arr
|
|
103
|
+
|> Belt.Array.keep(_, ((_, isOptional, x)) => !(isOptional && x == Js.Json.null))
|
|
104
|
+
|> Belt.Array.map(_, ((k, _, v)) => (k, v))
|
|
105
|
+
|
|
101
106
|
let optionFromJson = (decoder, json) =>
|
|
102
107
|
switch Js.Json.decodeNull(json) {
|
|
103
108
|
| Some(_) => Belt.Result.Ok(None)
|
|
@@ -5,28 +5,28 @@ var Jest = require("@glennsl/bs-jest/src/jest.js");
|
|
|
5
5
|
var Polyvariants = require("../src/Polyvariants.js");
|
|
6
6
|
|
|
7
7
|
Jest.describe("polymorphic variants with attribute", (function (param) {
|
|
8
|
-
Jest.test("encode
|
|
8
|
+
Jest.test("encode 하나", (function (param) {
|
|
9
9
|
var polyvariantEncoded = Polyvariants.t_encode("one");
|
|
10
10
|
return Jest.Expect.toEqual("하나", Jest.Expect.expect(polyvariantEncoded));
|
|
11
11
|
}));
|
|
12
|
-
Jest.test("encode
|
|
12
|
+
Jest.test("encode 둘", (function (param) {
|
|
13
13
|
var polyvariantEncoded = Polyvariants.t_encode("two");
|
|
14
14
|
return Jest.Expect.toEqual("둘", Jest.Expect.expect(polyvariantEncoded));
|
|
15
15
|
}));
|
|
16
|
-
Jest.test("decode
|
|
16
|
+
Jest.test("decode 하나", (function (param) {
|
|
17
17
|
var polyvariantDecoded = Polyvariants.t_decode("하나");
|
|
18
18
|
return Jest.Expect.toEqual({
|
|
19
19
|
TAG: /* Ok */0,
|
|
20
20
|
_0: "one"
|
|
21
21
|
}, Jest.Expect.expect(polyvariantDecoded));
|
|
22
22
|
}));
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
Jest.test("decode 둘", (function (param) {
|
|
24
|
+
var polyvariantDecoded = Polyvariants.t_decode("둘");
|
|
25
|
+
return Jest.Expect.toEqual({
|
|
26
|
+
TAG: /* Ok */0,
|
|
27
|
+
_0: "two"
|
|
28
|
+
}, Jest.Expect.expect(polyvariantDecoded));
|
|
29
|
+
}));
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
32
|
Jest.describe("polymorphic variants", (function (param) {
|
|
@@ -45,13 +45,13 @@ Jest.describe("polymorphic variants", (function (param) {
|
|
|
45
45
|
_0: "one"
|
|
46
46
|
}, Jest.Expect.expect(polyvariantDecoded));
|
|
47
47
|
}));
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
Jest.test("decode two", (function (param) {
|
|
49
|
+
var polyvariantDecoded = Polyvariants.t1_decode(["two"]);
|
|
50
|
+
return Jest.Expect.toEqual({
|
|
51
|
+
TAG: /* Ok */0,
|
|
52
|
+
_0: "two"
|
|
53
|
+
}, Jest.Expect.expect(polyvariantDecoded));
|
|
54
|
+
}));
|
|
55
55
|
}));
|
|
56
56
|
|
|
57
57
|
/* Not a pure module */
|
|
@@ -16,13 +16,13 @@ Jest.describe("record with @spice.key", (function (param) {
|
|
|
16
16
|
var encoded = Records.t_encode(sampleRecord);
|
|
17
17
|
return Jest.Expect.toEqual(sample, Jest.Expect.expect(encoded));
|
|
18
18
|
}));
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
Jest.test("decode", (function (param) {
|
|
20
|
+
var decoded = Records.t_decode(sample);
|
|
21
|
+
return Jest.Expect.toEqual({
|
|
22
|
+
TAG: /* Ok */0,
|
|
23
|
+
_0: sampleRecord
|
|
24
|
+
}, Jest.Expect.expect(decoded));
|
|
25
|
+
}));
|
|
26
26
|
}));
|
|
27
27
|
|
|
28
28
|
Jest.describe("record without @spice.key", (function (param) {
|
|
@@ -37,13 +37,64 @@ Jest.describe("record without @spice.key", (function (param) {
|
|
|
37
37
|
var encoded = Records.t1_encode(sampleRecord);
|
|
38
38
|
return Jest.Expect.toEqual(sample, Jest.Expect.expect(encoded));
|
|
39
39
|
}));
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
Jest.test("decode", (function (param) {
|
|
41
|
+
var decoded = Records.t1_decode(sample);
|
|
42
|
+
return Jest.Expect.toEqual({
|
|
43
|
+
TAG: /* Ok */0,
|
|
44
|
+
_0: sampleRecord
|
|
45
|
+
}, Jest.Expect.expect(decoded));
|
|
46
|
+
}));
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
Jest.describe("record with optional field", (function (param) {
|
|
50
|
+
var sample1 = {};
|
|
51
|
+
sample1["label"] = "sample";
|
|
52
|
+
sample1["value"] = 1.0;
|
|
53
|
+
var sampleRecord1 = {
|
|
54
|
+
label: "sample",
|
|
55
|
+
value: 1
|
|
56
|
+
};
|
|
57
|
+
Jest.test("encode", (function (param) {
|
|
58
|
+
var encoded = Records.tOp_encode(sampleRecord1);
|
|
59
|
+
return Jest.Expect.toEqual(sample1, Jest.Expect.expect(encoded));
|
|
60
|
+
}));
|
|
61
|
+
Jest.test("decode", (function (param) {
|
|
62
|
+
var decoded = Records.tOp_decode(sample1);
|
|
63
|
+
return Jest.Expect.toEqual({
|
|
64
|
+
TAG: /* Ok */0,
|
|
65
|
+
_0: sampleRecord1
|
|
66
|
+
}, Jest.Expect.expect(decoded));
|
|
67
|
+
}));
|
|
68
|
+
var sample2 = {};
|
|
69
|
+
sample2["label"] = "sample";
|
|
70
|
+
var sampleRecord2 = {
|
|
71
|
+
label: "sample"
|
|
72
|
+
};
|
|
73
|
+
Jest.test("encode omit optional field", (function (param) {
|
|
74
|
+
var encoded = Records.tOp_encode(sampleRecord2);
|
|
75
|
+
return Jest.Expect.toEqual(sample2, Jest.Expect.expect(encoded));
|
|
76
|
+
}));
|
|
77
|
+
Jest.test("decode omit optional field", (function (param) {
|
|
78
|
+
var decoded = Records.tOp_decode(sample2);
|
|
79
|
+
return Jest.Expect.toEqual({
|
|
80
|
+
TAG: /* Ok */0,
|
|
81
|
+
_0: sampleRecord2
|
|
82
|
+
}, Jest.Expect.expect(decoded));
|
|
83
|
+
}));
|
|
84
|
+
var sample3 = {};
|
|
85
|
+
sample3["label"] = null;
|
|
86
|
+
var sampleRecord3 = {};
|
|
87
|
+
Jest.test("encode omit optional field with None field", (function (param) {
|
|
88
|
+
var encoded = Records.tOp_encode(sampleRecord3);
|
|
89
|
+
return Jest.Expect.toEqual(sample3, Jest.Expect.expect(encoded));
|
|
90
|
+
}));
|
|
91
|
+
Jest.test("decode omit optional field with None field", (function (param) {
|
|
92
|
+
var decoded = Records.tOp_decode(sample3);
|
|
93
|
+
return Jest.Expect.toEqual({
|
|
94
|
+
TAG: /* Ok */0,
|
|
95
|
+
_0: sampleRecord3
|
|
96
|
+
}, Jest.Expect.expect(decoded));
|
|
97
|
+
}));
|
|
47
98
|
}));
|
|
48
99
|
|
|
49
100
|
/* Not a pure module */
|
|
@@ -49,3 +49,63 @@ describe("record without @spice.key", _ => {
|
|
|
49
49
|
expect(decoded) |> toEqual(Belt.Result.Ok(sampleRecord))
|
|
50
50
|
})
|
|
51
51
|
})
|
|
52
|
+
|
|
53
|
+
describe("record with optional field", _ => {
|
|
54
|
+
open Records
|
|
55
|
+
|
|
56
|
+
let sample1 = Js.Dict.empty()
|
|
57
|
+
sample1->Js.Dict.set("label", Js.Json.string("sample"))
|
|
58
|
+
sample1->Js.Dict.set("value", Js.Json.number(1.0))
|
|
59
|
+
let sampleJson1 = sample1->Js.Json.object_
|
|
60
|
+
|
|
61
|
+
let sampleRecord1: tOp = {
|
|
62
|
+
label: Some("sample"),
|
|
63
|
+
value: 1,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
test(`encode`, _ => {
|
|
67
|
+
let encoded = sampleRecord1->tOp_encode
|
|
68
|
+
expect(encoded) |> toEqual(sampleJson1)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test(`decode`, _ => {
|
|
72
|
+
let decoded = sampleJson1->Records.tOp_decode
|
|
73
|
+
expect(decoded) |> toEqual(Belt.Result.Ok(sampleRecord1))
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
let sample2 = Js.Dict.empty()
|
|
77
|
+
sample2->Js.Dict.set("label", Js.Json.string("sample"))
|
|
78
|
+
let sampleJson2 = sample2->Js.Json.object_
|
|
79
|
+
|
|
80
|
+
let sampleRecord2: tOp = {
|
|
81
|
+
label: Some("sample"),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
test(`encode omit optional field`, _ => {
|
|
85
|
+
let encoded = sampleRecord2->tOp_encode
|
|
86
|
+
expect(encoded) |> toEqual(sampleJson2)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test(`decode omit optional field`, _ => {
|
|
90
|
+
let decoded = sampleJson2->Records.tOp_decode
|
|
91
|
+
expect(decoded) |> toEqual(Belt.Result.Ok(sampleRecord2))
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
let sample3 = Js.Dict.empty()
|
|
95
|
+
sample3->Js.Dict.set("label", Js.Json.null)
|
|
96
|
+
let sampleJson3 = sample3->Js.Json.object_
|
|
97
|
+
|
|
98
|
+
let sampleRecord3: tOp = {
|
|
99
|
+
label: None,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
test(`encode omit optional field with None field`, _ => {
|
|
103
|
+
let encoded = sampleRecord3->tOp_encode
|
|
104
|
+
expect(encoded) |> toEqual(sampleJson3)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test(`decode omit optional field with None field`, _ => {
|
|
108
|
+
let decoded = sampleJson3->Records.tOp_decode
|
|
109
|
+
expect(decoded) |> toEqual(Belt.Result.Ok(sampleRecord3))
|
|
110
|
+
})
|
|
111
|
+
})
|