@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/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
- ( (match do_encode with
32
- | true -> Some [%expr Spice.stringToJson]
33
- | false -> None),
34
- match do_decode with
35
- | true -> Some [%expr Spice.stringFromJson]
36
- | false -> None ))
37
- | Lident "int" -> (
38
- ( (match do_encode with
39
- | true -> Some [%expr Spice.intToJson]
40
- | false -> None),
41
- match do_decode with
42
- | true -> Some [%expr Spice.intFromJson]
43
- | false -> None ))
44
- | Lident "int64" -> (
45
- ( (match do_encode with
46
- | true -> Some [%expr Spice.int64ToJson]
47
- | false -> None),
48
- match do_decode with
49
- | true -> Some [%expr Spice.int64FromJson]
50
- | false -> None ))
51
- | Lident "float" -> (
52
- ( (match do_encode with
53
- | true -> Some [%expr Spice.floatToJson]
54
- | false -> None),
55
- match do_decode with
56
- | true -> Some [%expr Spice.floatFromJson]
57
- | false -> None ))
58
- | Lident "bool" -> (
59
- ( (match do_encode with
60
- | true -> Some [%expr Spice.boolToJson]
61
- | false -> None),
62
- match do_decode with
63
- | true -> Some [%expr Spice.boolFromJson]
64
- | false -> None ))
65
- | Lident "unit" -> (
66
- ( (match do_encode with
67
- | true -> Some [%expr Spice.unitToJson]
68
- | false -> None),
69
- match do_decode with
70
- | true -> Some [%expr Spice.unitFromJson]
71
- | false -> None ))
72
- | Lident "array" -> (
73
- ( (match do_encode with
74
- | true -> Some [%expr Spice.arrayToJson]
75
- | false -> None),
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 ({ do_encode; do_decode } as generator_settings)
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
- ( (match do_encode with
146
- | true ->
147
- Some
148
- (composite_codecs
149
- |> List.map (fun (e, _) -> Option.get e)
150
- |> Tuple.generate_encoder)
151
- | false -> None),
152
- match do_decode with
153
- | true ->
154
- Some
155
- (composite_codecs
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
- ( (match do_encode with
174
- | true ->
175
- Some
176
- [%expr
177
- let e, _ = [%e expr] in
178
- e]
179
- | false -> None),
180
- match do_decode with
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)
@@ -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
- match do_encode with
223
- | true ->
224
- List.map
225
- (generate_encoder_case generator_settings unboxed has_attr_as)
226
- parsed_fields
227
- |> Exp.match_ [%expr v]
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 =
@@ -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
- [%expr [%e key], [%e Option.get encoder] [%e field]])
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 [%e arrExpr] |> Js.Dict.fromArray |> Js.Json.object_]
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 |> List.map (fun { name } -> (lid name, make_ident_expr name))
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
- ( (match do_encode with
128
- | true -> Some (generate_encoder parsed_decls unboxed)
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
@@ -188,15 +188,11 @@ let generate_codecs ({ do_encode; do_decode } as generator_settings)
188
188
  in
189
189
 
190
190
  let encoder =
191
- match do_encode with
192
- | true ->
193
- parsed_decls
194
- |> List.map
195
- (generate_encoder_case generator_settings unboxed has_attr_as)
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 =
@@ -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
+ ]
@@ -14,7 +14,7 @@ let error = (~path=?, message, value) => {
14
14
  | None => ""
15
15
  | Some(s) => s
16
16
  }
17
- Belt.Result.Error({path: path, message: message, value: value})
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 \xed\x95\x98\xeb\x82\x98", (function (param) {
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 \xeb\x91\x98", (function (param) {
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 \xed\x95\x98\xeb\x82\x98", (function (param) {
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
- return Jest.test("decode \xeb\x91\x98", (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
- }));
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
- return 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
- }));
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
- return 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
- }));
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
- return 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
- }));
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
+ })