@greenlabs/ppx-spice 0.1.8 → 0.1.9-rc1
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/CHANGELOG.md +4 -0
- package/README.md +4 -0
- package/docs/GUIDE.md +139 -0
- package/package.json +1 -1
- package/ppx-windows.exe +0 -0
- package/.github/workflows/build_linux.yml +0 -41
- package/.github/workflows/build_macos.yml +0 -41
- package/.github/workflows/build_windows.yml +0 -41
- package/.github/workflows/print_esy_cache.js +0 -13
- package/.github/workflows/publish.yml +0 -161
- package/.github/workflows/publish_prerelease.yml +0 -177
- package/.vscode/settings.json +0 -6
- package/src/.ocamlformat +0 -0
- package/src/bin/bin.ml +0 -1
- package/src/bin/dune +0 -5
- package/src/dune +0 -1
- package/src/dune-project +0 -2
- package/src/dune-workspace +0 -3
- package/src/esy.lock/.gitattributes +0 -3
- package/src/esy.lock/.gitignore +0 -3
- package/src/esy.lock/index.json +0 -1196
- package/src/esy.lock/opam/astring.0.8.5/opam +0 -37
- package/src/esy.lock/opam/base-bytes.base/opam +0 -9
- package/src/esy.lock/opam/base-threads.base/opam +0 -6
- package/src/esy.lock/opam/base-unix.base/opam +0 -6
- package/src/esy.lock/opam/base.v0.14.1/opam +0 -36
- package/src/esy.lock/opam/biniou.1.2.1/opam +0 -45
- package/src/esy.lock/opam/cmdliner.1.0.4/opam +0 -36
- package/src/esy.lock/opam/cppo.1.6.8/opam +0 -37
- package/src/esy.lock/opam/csexp.1.5.1/opam +0 -60
- package/src/esy.lock/opam/dot-merlin-reader.4.1/opam +0 -30
- package/src/esy.lock/opam/dune-build-info.2.9.1/opam +0 -42
- package/src/esy.lock/opam/dune-configurator.2.9.1/opam +0 -47
- package/src/esy.lock/opam/dune.2.9.1/opam +0 -58
- package/src/esy.lock/opam/easy-format.1.3.2/opam +0 -46
- package/src/esy.lock/opam/fix.20201120/opam +0 -24
- package/src/esy.lock/opam/fpath.0.7.3/opam +0 -36
- package/src/esy.lock/opam/menhir.20211012/opam +0 -28
- package/src/esy.lock/opam/menhirLib.20211012/opam +0 -29
- package/src/esy.lock/opam/menhirSdk.20211012/opam +0 -29
- package/src/esy.lock/opam/ocaml-compiler-libs.v0.12.4/opam +0 -39
- package/src/esy.lock/opam/ocaml-lsp-server.1.8.3/opam +0 -54
- package/src/esy.lock/opam/ocamlbuild.0.14.0/opam +0 -36
- package/src/esy.lock/opam/ocamlfind.1.9.1/opam +0 -44
- package/src/esy.lock/opam/ocamlformat-rpc-lib.0.18.0/opam +0 -40
- package/src/esy.lock/opam/ocamlformat.0.19.0/opam +0 -55
- package/src/esy.lock/opam/ocp-indent.1.8.1/opam +0 -57
- package/src/esy.lock/opam/odoc-parser.0.9.0/opam +0 -45
- package/src/esy.lock/opam/pp.1.1.2/opam +0 -58
- package/src/esy.lock/opam/ppx_derivers.1.2.1/opam +0 -23
- package/src/esy.lock/opam/ppx_yojson_conv_lib.v0.14.0/opam +0 -24
- package/src/esy.lock/opam/ppxlib.0.23.0/opam +0 -62
- package/src/esy.lock/opam/re.1.10.3/opam +0 -46
- package/src/esy.lock/opam/result.1.5/opam +0 -22
- package/src/esy.lock/opam/seq.base/files/META.seq +0 -4
- package/src/esy.lock/opam/seq.base/files/seq.install +0 -3
- package/src/esy.lock/opam/seq.base/opam +0 -15
- package/src/esy.lock/opam/sexplib0.v0.14.0/opam +0 -26
- package/src/esy.lock/opam/stdio.v0.14.0/opam +0 -27
- package/src/esy.lock/opam/stdlib-shims.0.3.0/opam +0 -31
- package/src/esy.lock/opam/topkg.1.0.4/opam +0 -44
- package/src/esy.lock/opam/uchar.0.0.2/opam +0 -36
- package/src/esy.lock/opam/uucp.14.0.0/opam +0 -41
- package/src/esy.lock/opam/uuseg.14.0.0/opam +0 -43
- package/src/esy.lock/opam/uutf.1.0.2/opam +0 -40
- package/src/esy.lock/opam/yojson.1.7.0/opam +0 -38
- package/src/esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override/files/ocamlbuild-0.14.0.patch +0 -463
- package/src/esy.lock/overrides/opam__s__ocamlbuild_opam__c__0.14.0_opam_override/package.json +0 -27
- package/src/esy.lock/overrides/opam__s__ocamlfind_opam__c__1.9.1_opam_override/files/findlib-1.9.1.patch +0 -471
- package/src/esy.lock/overrides/opam__s__ocamlfind_opam__c__1.9.1_opam_override/package.json +0 -61
- package/src/ppx/codecs.ml +0 -120
- package/src/ppx/decode_cases.ml +0 -18
- package/src/ppx/dune +0 -9
- package/src/ppx/polyvariants.ml +0 -288
- package/src/ppx/ppx_spice.ml +0 -19
- package/src/ppx/records.ml +0 -163
- package/src/ppx/signature.ml +0 -86
- package/src/ppx/structure.ml +0 -109
- package/src/ppx/tuple.ml +0 -68
- package/src/ppx/utils.ml +0 -117
- package/src/ppx/variants.ml +0 -257
- package/src/ppx_spice.opam +0 -21
- package/test/__tests__/test_optional_field_records.js +0 -91
- package/test/__tests__/test_optional_field_records.res +0 -98
- package/test/__tests__/test_polyvariants.js +0 -57
- package/test/__tests__/test_polyvariants.res +0 -41
- package/test/__tests__/test_records.js +0 -100
- package/test/__tests__/test_records.res +0 -111
- package/test/__tests__/test_variants.js +0 -85
- package/test/__tests__/test_variants.res +0 -63
- package/test/bsconfig.json +0 -27
- package/test/package.json +0 -18
- package/test/src/OptionalFieldRecords.js +0 -237
- package/test/src/OptionalFieldRecords.res +0 -23
- package/test/src/Polyvariants.js +0 -94
- package/test/src/Polyvariants.res +0 -5
- package/test/src/Polyvariants.resi +0 -5
- package/test/src/Records.js +0 -186
- package/test/src/Records.res +0 -17
- package/test/src/Records.resi +0 -17
- package/test/src/Spice.js +0 -448
- package/test/src/Spice_Codecs.js +0 -57
- package/test/src/Variants.js +0 -115
- package/test/src/Variants.res +0 -11
- package/test/src/Variants.resi +0 -11
- package/test/yarn.lock +0 -4194
package/src/ppx/tuple.ml
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
open Ppxlib
|
|
2
|
-
open Parsetree
|
|
3
|
-
open Ast_helper
|
|
4
|
-
open Utils
|
|
5
|
-
|
|
6
|
-
let generate_encoder composite_encoders =
|
|
7
|
-
let arrExp =
|
|
8
|
-
composite_encoders
|
|
9
|
-
|> List.mapi (fun i e ->
|
|
10
|
-
let vExp = Exp.ident (lid ("v" ^ string_of_int i)) in
|
|
11
|
-
[%expr [%e e] [%e vExp]])
|
|
12
|
-
|> Exp.array
|
|
13
|
-
in
|
|
14
|
-
let deconstructor_pattern =
|
|
15
|
-
composite_encoders
|
|
16
|
-
|> List.mapi (fun i _ -> Pat.var (mknoloc ("v" ^ string_of_int i)))
|
|
17
|
-
|> Pat.tuple
|
|
18
|
-
in
|
|
19
|
-
[%expr fun [%p deconstructor_pattern] -> [%e arrExp] |> Js.Json.array]
|
|
20
|
-
|
|
21
|
-
let generate_decode_success_case num_args =
|
|
22
|
-
{
|
|
23
|
-
pc_lhs =
|
|
24
|
-
Array.init num_args (fun i ->
|
|
25
|
-
mknoloc ("v" ^ string_of_int i) |> Pat.var |> fun p ->
|
|
26
|
-
[%pat? Belt.Result.Ok [%p p]])
|
|
27
|
-
|> Array.to_list
|
|
28
|
-
|> tuple_or_singleton Pat.tuple;
|
|
29
|
-
pc_guard = None;
|
|
30
|
-
pc_rhs =
|
|
31
|
-
( Array.init num_args (fun i -> make_ident_expr ("v" ^ string_of_int i))
|
|
32
|
-
|> Array.to_list |> Exp.tuple
|
|
33
|
-
|> fun e -> [%expr Belt.Result.Ok [%e e]] );
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
let generate_decode_switch composite_decoders =
|
|
37
|
-
let decode_expr =
|
|
38
|
-
composite_decoders
|
|
39
|
-
|> List.mapi (fun i d ->
|
|
40
|
-
let ident = make_ident_expr ("v" ^ string_of_int i) in
|
|
41
|
-
[%expr [%e d] [%e ident]])
|
|
42
|
-
|> Exp.tuple
|
|
43
|
-
in
|
|
44
|
-
composite_decoders
|
|
45
|
-
|> List.mapi
|
|
46
|
-
(Decode_cases.generate_error_case (List.length composite_decoders))
|
|
47
|
-
|> List.append
|
|
48
|
-
[ generate_decode_success_case (List.length composite_decoders) ]
|
|
49
|
-
|> Exp.match_ decode_expr
|
|
50
|
-
|
|
51
|
-
let generate_decoder composite_decoders =
|
|
52
|
-
let match_arr_pattern =
|
|
53
|
-
composite_decoders
|
|
54
|
-
|> List.mapi (fun i _ -> Pat.var (mknoloc ("v" ^ string_of_int i)))
|
|
55
|
-
|> Pat.array
|
|
56
|
-
in
|
|
57
|
-
let match_pattern = [%pat? Js.Json.JSONArray [%p match_arr_pattern]] in
|
|
58
|
-
let outer_switch =
|
|
59
|
-
Exp.match_ [%expr Js.Json.classify json]
|
|
60
|
-
[
|
|
61
|
-
Exp.case match_pattern (generate_decode_switch composite_decoders);
|
|
62
|
-
Exp.case
|
|
63
|
-
[%pat? Js.Json.JSONArray _]
|
|
64
|
-
[%expr Spice.error "Incorrect cardinality" json];
|
|
65
|
-
Exp.case [%pat? _] [%expr Spice.error "Not a tuple" json];
|
|
66
|
-
]
|
|
67
|
-
in
|
|
68
|
-
[%expr fun json -> [%e outer_switch]]
|
package/src/ppx/utils.ml
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
open Ppxlib
|
|
2
|
-
open Parsetree
|
|
3
|
-
open Ast_helper
|
|
4
|
-
|
|
5
|
-
let annotation_name = "spice"
|
|
6
|
-
|
|
7
|
-
let encoder_func_suffix = "_encode"
|
|
8
|
-
|
|
9
|
-
let decoder_func_suffix = "_decode"
|
|
10
|
-
|
|
11
|
-
let encoder_var_prefix = "encoder_"
|
|
12
|
-
|
|
13
|
-
let decoder_var_prefix = "decoder_"
|
|
14
|
-
|
|
15
|
-
let loc = !default_loc
|
|
16
|
-
|
|
17
|
-
let fail loc message = Location.raise_errorf ~loc "%s" message
|
|
18
|
-
|
|
19
|
-
let longident_parse = Longident.parse [@@ocaml.warning "-3"]
|
|
20
|
-
|
|
21
|
-
let mkloc txt loc = { Location.txt; loc }
|
|
22
|
-
|
|
23
|
-
let mknoloc txt = mkloc txt Location.none
|
|
24
|
-
|
|
25
|
-
let lid ?(loc = Location.none) s = mkloc (Longident.parse s) loc
|
|
26
|
-
|
|
27
|
-
let make_ident_expr ?attrs s = Exp.ident ?attrs (mknoloc (longident_parse s))
|
|
28
|
-
|
|
29
|
-
let tuple_or_singleton tuple l =
|
|
30
|
-
match List.length l > 1 with true -> tuple l | false -> List.hd l
|
|
31
|
-
|
|
32
|
-
let get_attribute_by_name attributes name =
|
|
33
|
-
let filtered =
|
|
34
|
-
attributes
|
|
35
|
-
|> List.filter (fun { attr_name = { Location.txt } } -> txt = name)
|
|
36
|
-
in
|
|
37
|
-
match filtered with
|
|
38
|
-
| [] -> Ok None
|
|
39
|
-
| [ attribute ] -> Ok (Some attribute)
|
|
40
|
-
| _ -> Error ("Too many occurrences of \"" ^ name ^ "\" attribute")
|
|
41
|
-
|
|
42
|
-
type generator_settings = { do_encode : bool; do_decode : bool }
|
|
43
|
-
|
|
44
|
-
let get_generator_settings_from_attributes attributes =
|
|
45
|
-
match get_attribute_by_name attributes annotation_name with
|
|
46
|
-
| Ok None -> (
|
|
47
|
-
match
|
|
48
|
-
( get_attribute_by_name attributes (annotation_name ^ ".decode"),
|
|
49
|
-
get_attribute_by_name attributes (annotation_name ^ ".encode") )
|
|
50
|
-
with
|
|
51
|
-
| Ok (Some _), Ok (Some _) ->
|
|
52
|
-
Ok (Some { do_encode = true; do_decode = true })
|
|
53
|
-
| Ok (Some _), Ok None ->
|
|
54
|
-
Ok (Some { do_encode = false; do_decode = true })
|
|
55
|
-
| Ok None, Ok (Some _) ->
|
|
56
|
-
Ok (Some { do_encode = true; do_decode = false })
|
|
57
|
-
| Ok None, Ok None -> Ok None
|
|
58
|
-
| (Error _ as e), _ -> e
|
|
59
|
-
| _, (Error _ as e) -> e)
|
|
60
|
-
| Ok (Some _) -> Ok (Some { do_encode = true; do_decode = true })
|
|
61
|
-
| Error _ as e -> e
|
|
62
|
-
|
|
63
|
-
let get_expression_from_payload { attr_name = { loc }; attr_payload = payload }
|
|
64
|
-
=
|
|
65
|
-
match payload with
|
|
66
|
-
| PStr [ { pstr_desc } ] -> (
|
|
67
|
-
match pstr_desc with
|
|
68
|
-
| Pstr_eval (expr, _) -> expr
|
|
69
|
-
| _ -> fail loc "Expected expression as attribute payload")
|
|
70
|
-
| _ -> fail loc "Expected expression as attribute payload"
|
|
71
|
-
|
|
72
|
-
let get_param_names params =
|
|
73
|
-
params
|
|
74
|
-
|> List.map (fun ({ ptyp_desc; ptyp_loc }, _) ->
|
|
75
|
-
match ptyp_desc with
|
|
76
|
-
| Ptyp_var s -> s
|
|
77
|
-
| _ ->
|
|
78
|
-
fail ptyp_loc "Unhandled param type" |> fun v ->
|
|
79
|
-
Location.Error v |> raise)
|
|
80
|
-
|
|
81
|
-
let get_string_from_expression { pexp_desc; pexp_loc } =
|
|
82
|
-
match pexp_desc with
|
|
83
|
-
| Pexp_constant const -> (
|
|
84
|
-
match const with
|
|
85
|
-
| Pconst_string (name, loc, delimit) -> (name, loc, delimit)
|
|
86
|
-
| _ -> fail pexp_loc "cannot find a name??")
|
|
87
|
-
| _ -> fail pexp_loc "cannot find a name??"
|
|
88
|
-
|
|
89
|
-
let index_const i =
|
|
90
|
-
Pconst_string ("[" ^ string_of_int i ^ "]", Location.none, None)
|
|
91
|
-
|> Exp.constant
|
|
92
|
-
|
|
93
|
-
let rec is_identifier_used_in_core_type type_name { ptyp_desc; ptyp_loc } =
|
|
94
|
-
match ptyp_desc with
|
|
95
|
-
| Ptyp_arrow (_, _, _) ->
|
|
96
|
-
fail ptyp_loc "Can't generate codecs for function type"
|
|
97
|
-
| Ptyp_any -> fail ptyp_loc "Can't generate codecs for `any` type"
|
|
98
|
-
| Ptyp_package _ -> fail ptyp_loc "Can't generate codecs for module type"
|
|
99
|
-
| Ptyp_variant (_, _, _) -> fail ptyp_loc "Unexpected Ptyp_variant"
|
|
100
|
-
| Ptyp_var _ -> false
|
|
101
|
-
| Ptyp_tuple child_types ->
|
|
102
|
-
List.exists (is_identifier_used_in_core_type type_name) child_types
|
|
103
|
-
| Ptyp_constr ({ txt }, child_types) -> (
|
|
104
|
-
match txt = Lident type_name with
|
|
105
|
-
| true -> true
|
|
106
|
-
| false ->
|
|
107
|
-
List.exists (is_identifier_used_in_core_type type_name) child_types)
|
|
108
|
-
| _ -> fail ptyp_loc "This syntax is not yet handled by spice"
|
|
109
|
-
|
|
110
|
-
let attr_warning expr =
|
|
111
|
-
{
|
|
112
|
-
attr_name = mkloc "ocaml.warning" loc;
|
|
113
|
-
attr_payload = PStr [ { pstr_desc = Pstr_eval (expr, []); pstr_loc = loc } ];
|
|
114
|
-
attr_loc = loc;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
let some_if_true cond a = match cond with true -> Some a | false -> None
|
package/src/ppx/variants.ml
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
open Ppxlib
|
|
2
|
-
open Parsetree
|
|
3
|
-
open Ast_helper
|
|
4
|
-
open Utils
|
|
5
|
-
|
|
6
|
-
type parsed_decl = {
|
|
7
|
-
name : string;
|
|
8
|
-
alias : expression;
|
|
9
|
-
has_attr_as : bool;
|
|
10
|
-
constr_decl : Parsetree.constructor_declaration;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
let generate_encoder_case generator_settings unboxed has_attr_as
|
|
14
|
-
{ name; alias; constr_decl = { pcd_args; pcd_loc } } =
|
|
15
|
-
match pcd_args with
|
|
16
|
-
| Pcstr_tuple args ->
|
|
17
|
-
let alias_name, _, delimit = get_string_from_expression alias in
|
|
18
|
-
let constructor_expr =
|
|
19
|
-
Exp.constant (Pconst_string (alias_name, Location.none, delimit))
|
|
20
|
-
in
|
|
21
|
-
let lhs_vars =
|
|
22
|
-
match args with
|
|
23
|
-
| [] -> None
|
|
24
|
-
| [ _ ] -> Some (Pat.var (mknoloc "v0"))
|
|
25
|
-
| _ ->
|
|
26
|
-
args
|
|
27
|
-
|> List.mapi (fun i _ ->
|
|
28
|
-
mkloc ("v" ^ string_of_int i) pcd_loc |> Pat.var)
|
|
29
|
-
|> Pat.tuple
|
|
30
|
-
|> fun v -> Some v
|
|
31
|
-
in
|
|
32
|
-
let rhs_list =
|
|
33
|
-
args
|
|
34
|
-
|> List.map (Codecs.generate_codecs generator_settings)
|
|
35
|
-
|> List.map (fun (encoder, _) -> Option.get encoder)
|
|
36
|
-
|> List.mapi (fun i e ->
|
|
37
|
-
Exp.apply ~loc:pcd_loc e
|
|
38
|
-
[ (Asttypes.Nolabel, make_ident_expr ("v" ^ string_of_int i)) ])
|
|
39
|
-
|> List.append [ [%expr Js.Json.string [%e constructor_expr]] ]
|
|
40
|
-
in
|
|
41
|
-
|
|
42
|
-
{
|
|
43
|
-
pc_lhs = Pat.construct (lid name) lhs_vars;
|
|
44
|
-
pc_guard = None;
|
|
45
|
-
pc_rhs =
|
|
46
|
-
(if unboxed then List.tl rhs_list |> List.hd
|
|
47
|
-
else if has_attr_as then [%expr Js.Json.string [%e constructor_expr]]
|
|
48
|
-
else [%expr Js.Json.array [%e rhs_list |> Exp.array]]);
|
|
49
|
-
}
|
|
50
|
-
| Pcstr_record _ -> fail pcd_loc "This syntax is not yet implemented by spice"
|
|
51
|
-
|
|
52
|
-
let generate_decode_success_case num_args constructor_name =
|
|
53
|
-
{
|
|
54
|
-
pc_lhs =
|
|
55
|
-
Array.init num_args (fun i ->
|
|
56
|
-
mknoloc ("v" ^ string_of_int i) |> Pat.var |> fun p ->
|
|
57
|
-
[%pat? Belt.Result.Ok [%p p]])
|
|
58
|
-
|> Array.to_list
|
|
59
|
-
|> tuple_or_singleton Pat.tuple;
|
|
60
|
-
pc_guard = None;
|
|
61
|
-
pc_rhs =
|
|
62
|
-
( Array.init num_args (fun i -> make_ident_expr ("v" ^ string_of_int i))
|
|
63
|
-
|> Array.to_list
|
|
64
|
-
|> tuple_or_singleton Exp.tuple
|
|
65
|
-
|> fun v ->
|
|
66
|
-
Some v |> Exp.construct (lid constructor_name) |> fun e ->
|
|
67
|
-
[%expr Belt.Result.Ok [%e e]] );
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
let generate_arg_decoder generator_settings args constructor_name =
|
|
71
|
-
let num_args = List.length args in
|
|
72
|
-
args
|
|
73
|
-
|> List.mapi (Decode_cases.generate_error_case num_args)
|
|
74
|
-
|> List.append [ generate_decode_success_case num_args constructor_name ]
|
|
75
|
-
|> Exp.match_
|
|
76
|
-
(args
|
|
77
|
-
|> List.map (Codecs.generate_codecs generator_settings)
|
|
78
|
-
|> List.mapi (fun i (_, decoder) ->
|
|
79
|
-
Exp.apply (Option.get decoder)
|
|
80
|
-
[
|
|
81
|
-
( Asttypes.Nolabel,
|
|
82
|
-
(* +1 because index 0 is the constructor *)
|
|
83
|
-
let idx =
|
|
84
|
-
Pconst_integer (string_of_int (i + 1), None)
|
|
85
|
-
|> Exp.constant
|
|
86
|
-
in
|
|
87
|
-
[%expr Belt.Array.getExn json_arr [%e idx]] );
|
|
88
|
-
])
|
|
89
|
-
|> tuple_or_singleton Exp.tuple)
|
|
90
|
-
|
|
91
|
-
let generate_decoder_case generator_settings
|
|
92
|
-
{ pcd_name = { txt = name }; pcd_args; pcd_loc } =
|
|
93
|
-
match pcd_args with
|
|
94
|
-
| Pcstr_tuple args ->
|
|
95
|
-
let arg_len =
|
|
96
|
-
Pconst_integer (string_of_int (List.length args + 1), None)
|
|
97
|
-
|> Exp.constant
|
|
98
|
-
in
|
|
99
|
-
let decoded =
|
|
100
|
-
match args with
|
|
101
|
-
| [] ->
|
|
102
|
-
let ident = lid name in
|
|
103
|
-
[%expr Belt.Result.Ok [%e Exp.construct ident None]]
|
|
104
|
-
| _ -> generate_arg_decoder generator_settings args name
|
|
105
|
-
in
|
|
106
|
-
|
|
107
|
-
{
|
|
108
|
-
pc_lhs =
|
|
109
|
-
( Pconst_string (name, Location.none, None) |> Pat.constant |> fun v ->
|
|
110
|
-
Some v |> Pat.construct (lid "Js.Json.JSONString") );
|
|
111
|
-
pc_guard = None;
|
|
112
|
-
pc_rhs =
|
|
113
|
-
[%expr
|
|
114
|
-
if Js.Array.length tagged <> [%e arg_len] then
|
|
115
|
-
Spice.error "Invalid number of arguments to variant constructor" v
|
|
116
|
-
else [%e decoded]];
|
|
117
|
-
}
|
|
118
|
-
| Pcstr_record _ -> fail pcd_loc "This syntax is not yet implemented by spice"
|
|
119
|
-
|
|
120
|
-
let generate_decoder_case_attr generator_settings
|
|
121
|
-
{ name; alias; constr_decl = { pcd_args; pcd_loc } } =
|
|
122
|
-
match pcd_args with
|
|
123
|
-
| Pcstr_tuple args ->
|
|
124
|
-
let alias_name, _, delimit = get_string_from_expression alias in
|
|
125
|
-
let decoded =
|
|
126
|
-
match args with
|
|
127
|
-
| [] ->
|
|
128
|
-
let ident = lid name in
|
|
129
|
-
[%expr Belt.Result.Ok [%e Exp.construct ident None]]
|
|
130
|
-
| _ -> generate_arg_decoder generator_settings args name
|
|
131
|
-
in
|
|
132
|
-
|
|
133
|
-
let if' =
|
|
134
|
-
Exp.apply (make_ident_expr "=")
|
|
135
|
-
[
|
|
136
|
-
( Asttypes.Nolabel,
|
|
137
|
-
Pconst_string (alias_name, Location.none, delimit) |> Exp.constant
|
|
138
|
-
);
|
|
139
|
-
(Asttypes.Nolabel, [%expr str]);
|
|
140
|
-
]
|
|
141
|
-
in
|
|
142
|
-
let then' = [%expr [%e decoded]] in
|
|
143
|
-
|
|
144
|
-
(if', then')
|
|
145
|
-
| Pcstr_record _ -> fail pcd_loc "This syntax is not yet implemented by spice"
|
|
146
|
-
|
|
147
|
-
let generate_unboxed_decode generator_settings
|
|
148
|
-
{ pcd_name = { txt = name }; pcd_args; pcd_loc } =
|
|
149
|
-
match pcd_args with
|
|
150
|
-
| Pcstr_tuple args -> (
|
|
151
|
-
match args with
|
|
152
|
-
| [ a ] -> (
|
|
153
|
-
let _, d = Codecs.generate_codecs generator_settings a in
|
|
154
|
-
match d with
|
|
155
|
-
| Some d ->
|
|
156
|
-
let constructor = Exp.construct (lid name) (Some [%expr v]) in
|
|
157
|
-
|
|
158
|
-
Some
|
|
159
|
-
[%expr
|
|
160
|
-
fun v ->
|
|
161
|
-
Belt.Result.map ([%e d] v) (fun v -> [%e constructor])]
|
|
162
|
-
| None -> None)
|
|
163
|
-
| _ -> fail pcd_loc "Expected exactly one type argument")
|
|
164
|
-
| Pcstr_record _ -> fail pcd_loc "This syntax is not yet implemented by spice"
|
|
165
|
-
|
|
166
|
-
let parse_decl _generator_settings
|
|
167
|
-
({ pcd_name = { txt }; pcd_loc; pcd_attributes } as constr_decl) =
|
|
168
|
-
let alias, has_attr_as =
|
|
169
|
-
match get_attribute_by_name pcd_attributes "spice.as" with
|
|
170
|
-
| Ok (Some attribute) -> (get_expression_from_payload attribute, true)
|
|
171
|
-
| Ok None -> (Exp.constant (Pconst_string (txt, Location.none, None)), false)
|
|
172
|
-
| Error s -> (fail pcd_loc s, false)
|
|
173
|
-
in
|
|
174
|
-
|
|
175
|
-
{ name = txt; alias; has_attr_as; constr_decl }
|
|
176
|
-
|
|
177
|
-
let generate_codecs ({ do_encode; do_decode } as generator_settings)
|
|
178
|
-
constr_decls unboxed =
|
|
179
|
-
let parsed_decls = List.map (parse_decl generator_settings) constr_decls in
|
|
180
|
-
let count_has_attr =
|
|
181
|
-
parsed_decls |> List.filter (fun v -> v.has_attr_as) |> List.length
|
|
182
|
-
in
|
|
183
|
-
let has_attr_as =
|
|
184
|
-
if count_has_attr > 0 then
|
|
185
|
-
if count_has_attr = List.length parsed_decls then true
|
|
186
|
-
else failwith "Partial @spice.as usage is not allowed"
|
|
187
|
-
else false
|
|
188
|
-
in
|
|
189
|
-
|
|
190
|
-
let encoder =
|
|
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])
|
|
196
|
-
in
|
|
197
|
-
|
|
198
|
-
let decoder =
|
|
199
|
-
match not do_decode with
|
|
200
|
-
| true -> None
|
|
201
|
-
| false ->
|
|
202
|
-
if unboxed then
|
|
203
|
-
generate_unboxed_decode generator_settings (List.hd constr_decls)
|
|
204
|
-
else if has_attr_as then
|
|
205
|
-
let rec make_ifthenelse cases =
|
|
206
|
-
match cases with
|
|
207
|
-
| [] -> [%expr Spice.error "Not matched" v]
|
|
208
|
-
| hd :: tl ->
|
|
209
|
-
let if_, then_ = hd in
|
|
210
|
-
Exp.ifthenelse if_ then_ (Some (make_ifthenelse tl))
|
|
211
|
-
in
|
|
212
|
-
|
|
213
|
-
let decoder_switch =
|
|
214
|
-
List.map
|
|
215
|
-
(generate_decoder_case_attr generator_settings)
|
|
216
|
-
parsed_decls
|
|
217
|
-
|> make_ifthenelse
|
|
218
|
-
in
|
|
219
|
-
|
|
220
|
-
Some
|
|
221
|
-
[%expr
|
|
222
|
-
fun v ->
|
|
223
|
-
match Js.Json.classify v with
|
|
224
|
-
| Js.Json.JSONString str -> [%e decoder_switch]
|
|
225
|
-
| _ -> Spice.error "Not a JSONString" v]
|
|
226
|
-
else
|
|
227
|
-
let decoder_default_case =
|
|
228
|
-
{
|
|
229
|
-
pc_lhs = [%pat? _];
|
|
230
|
-
pc_guard = None;
|
|
231
|
-
pc_rhs =
|
|
232
|
-
[%expr
|
|
233
|
-
Spice.error "Invalid variant constructor"
|
|
234
|
-
(Belt.Array.getExn json_arr 0)];
|
|
235
|
-
}
|
|
236
|
-
in
|
|
237
|
-
|
|
238
|
-
let decoder_switch =
|
|
239
|
-
constr_decls |> List.map (generate_decoder_case generator_settings)
|
|
240
|
-
|> fun l ->
|
|
241
|
-
l @ [ decoder_default_case ]
|
|
242
|
-
|> Exp.match_ [%expr Belt.Array.getExn tagged 0]
|
|
243
|
-
in
|
|
244
|
-
|
|
245
|
-
Some
|
|
246
|
-
[%expr
|
|
247
|
-
fun v ->
|
|
248
|
-
match Js.Json.classify v with
|
|
249
|
-
| Js.Json.JSONArray [||] ->
|
|
250
|
-
Spice.error "Expected variant, found empty array" v
|
|
251
|
-
| Js.Json.JSONArray json_arr ->
|
|
252
|
-
let tagged = Js.Array.map Js.Json.classify json_arr in
|
|
253
|
-
[%e decoder_switch]
|
|
254
|
-
| _ -> Spice.error "Not a variant" v]
|
|
255
|
-
in
|
|
256
|
-
|
|
257
|
-
(encoder, decoder)
|
package/src/ppx_spice.opam
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
opam-version: "2.0"
|
|
2
|
-
name: "ppx_spice"
|
|
3
|
-
version: "0.1.8"
|
|
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
|
-
]
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
var Jest = require("@glennsl/bs-jest/src/jest.js");
|
|
5
|
-
var OptionalFieldRecords = require("../src/OptionalFieldRecords.js");
|
|
6
|
-
|
|
7
|
-
Jest.describe("optional field record", (function (param) {
|
|
8
|
-
var sample = {};
|
|
9
|
-
sample["a"] = 1;
|
|
10
|
-
sample["b"] = 1;
|
|
11
|
-
var sampleRecord = {
|
|
12
|
-
a: 1,
|
|
13
|
-
b: 1
|
|
14
|
-
};
|
|
15
|
-
Jest.test("encode", (function (param) {
|
|
16
|
-
var encoded = OptionalFieldRecords.t0_encode(sampleRecord);
|
|
17
|
-
return Jest.Expect.toEqual(sample, Jest.Expect.expect(encoded));
|
|
18
|
-
}));
|
|
19
|
-
Jest.test("decode", (function (param) {
|
|
20
|
-
var decoded = OptionalFieldRecords.t0_decode(sample);
|
|
21
|
-
return Jest.Expect.toEqual({
|
|
22
|
-
TAG: /* Ok */0,
|
|
23
|
-
_0: sampleRecord
|
|
24
|
-
}, Jest.Expect.expect(decoded));
|
|
25
|
-
}));
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
Jest.describe("optional field record: array<int>", (function (param) {
|
|
29
|
-
var sample = {};
|
|
30
|
-
sample["a"] = 1;
|
|
31
|
-
sample["bs"] = [1];
|
|
32
|
-
var sampleRecord_bs = [1];
|
|
33
|
-
var sampleRecord = {
|
|
34
|
-
a: 1,
|
|
35
|
-
bs: sampleRecord_bs
|
|
36
|
-
};
|
|
37
|
-
Jest.test("encode", (function (param) {
|
|
38
|
-
var encoded = OptionalFieldRecords.t1_encode(sampleRecord);
|
|
39
|
-
return Jest.Expect.toEqual(sample, Jest.Expect.expect(encoded));
|
|
40
|
-
}));
|
|
41
|
-
Jest.test("decode", (function (param) {
|
|
42
|
-
var decoded = OptionalFieldRecords.t1_decode(sample);
|
|
43
|
-
return Jest.Expect.toEqual({
|
|
44
|
-
TAG: /* Ok */0,
|
|
45
|
-
_0: sampleRecord
|
|
46
|
-
}, Jest.Expect.expect(decoded));
|
|
47
|
-
}));
|
|
48
|
-
}));
|
|
49
|
-
|
|
50
|
-
Jest.describe("optional field record: array<variant>", (function (param) {
|
|
51
|
-
var sample = {};
|
|
52
|
-
sample["a"] = 1;
|
|
53
|
-
sample["bs"] = ["B1"];
|
|
54
|
-
var sampleRecord_bs = [/* B1 */1];
|
|
55
|
-
var sampleRecord = {
|
|
56
|
-
a: 1,
|
|
57
|
-
bs: sampleRecord_bs
|
|
58
|
-
};
|
|
59
|
-
Jest.test("encode", (function (param) {
|
|
60
|
-
var encoded = OptionalFieldRecords.t2_encode(sampleRecord);
|
|
61
|
-
return Jest.Expect.toEqual(sample, Jest.Expect.expect(encoded));
|
|
62
|
-
}));
|
|
63
|
-
Jest.test("decode", (function (param) {
|
|
64
|
-
var decoded = OptionalFieldRecords.t2_decode(sample);
|
|
65
|
-
return Jest.Expect.toEqual({
|
|
66
|
-
TAG: /* Ok */0,
|
|
67
|
-
_0: sampleRecord
|
|
68
|
-
}, Jest.Expect.expect(decoded));
|
|
69
|
-
}));
|
|
70
|
-
}));
|
|
71
|
-
|
|
72
|
-
Jest.describe("optional field record: omit array<variant>", (function (param) {
|
|
73
|
-
var sample = {};
|
|
74
|
-
sample["a"] = 1;
|
|
75
|
-
var sampleRecord = {
|
|
76
|
-
a: 1
|
|
77
|
-
};
|
|
78
|
-
Jest.test("encode", (function (param) {
|
|
79
|
-
var encoded = OptionalFieldRecords.t2_encode(sampleRecord);
|
|
80
|
-
return Jest.Expect.toEqual(sample, Jest.Expect.expect(encoded));
|
|
81
|
-
}));
|
|
82
|
-
Jest.test("decode", (function (param) {
|
|
83
|
-
var decoded = OptionalFieldRecords.t2_decode(sample);
|
|
84
|
-
return Jest.Expect.toEqual({
|
|
85
|
-
TAG: /* Ok */0,
|
|
86
|
-
_0: sampleRecord
|
|
87
|
-
}, Jest.Expect.expect(decoded));
|
|
88
|
-
}));
|
|
89
|
-
}));
|
|
90
|
-
|
|
91
|
-
/* Not a pure module */
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
open Jest
|
|
2
|
-
open Expect
|
|
3
|
-
// open Belt
|
|
4
|
-
|
|
5
|
-
describe("optional field record", _ => {
|
|
6
|
-
open OptionalFieldRecords
|
|
7
|
-
|
|
8
|
-
let sample = Js.Dict.empty()
|
|
9
|
-
sample->Js.Dict.set("a", Js.Json.number(1.))
|
|
10
|
-
sample->Js.Dict.set("b", Js.Json.number(1.))
|
|
11
|
-
let sampleJson = sample->Js.Json.object_
|
|
12
|
-
|
|
13
|
-
let sampleRecord: t0 = {
|
|
14
|
-
a: 1,
|
|
15
|
-
b: 1,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
test(`encode`, _ => {
|
|
19
|
-
let encoded = sampleRecord->t0_encode
|
|
20
|
-
expect(encoded) |> toEqual(sampleJson)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
test(`decode`, _ => {
|
|
24
|
-
let decoded = sampleJson->t0_decode
|
|
25
|
-
expect(decoded) |> toEqual(Belt.Result.Ok(sampleRecord))
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
describe("optional field record: array<int>", _ => {
|
|
30
|
-
open OptionalFieldRecords
|
|
31
|
-
|
|
32
|
-
let sample = Js.Dict.empty()
|
|
33
|
-
sample->Js.Dict.set("a", Js.Json.number(1.))
|
|
34
|
-
sample->Js.Dict.set("bs", Js.Json.array([Js.Json.number(1.)]))
|
|
35
|
-
let sampleJson = sample->Js.Json.object_
|
|
36
|
-
|
|
37
|
-
let sampleRecord: t1 = {
|
|
38
|
-
a: 1,
|
|
39
|
-
bs: [1],
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
test(`encode`, _ => {
|
|
43
|
-
let encoded = sampleRecord->t1_encode
|
|
44
|
-
expect(encoded) |> toEqual(sampleJson)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test(`decode`, _ => {
|
|
48
|
-
let decoded = sampleJson->t1_decode
|
|
49
|
-
expect(decoded) |> toEqual(Belt.Result.Ok(sampleRecord))
|
|
50
|
-
})
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
describe("optional field record: array<variant>", _ => {
|
|
54
|
-
open OptionalFieldRecords
|
|
55
|
-
|
|
56
|
-
let sample = Js.Dict.empty()
|
|
57
|
-
sample->Js.Dict.set("a", Js.Json.number(1.))
|
|
58
|
-
sample->Js.Dict.set("bs", Js.Json.array([Js.Json.string("B1")]))
|
|
59
|
-
let sampleJson = sample->Js.Json.object_
|
|
60
|
-
|
|
61
|
-
let sampleRecord: t2 = {
|
|
62
|
-
a: 1,
|
|
63
|
-
bs: [B1],
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
test(`encode`, _ => {
|
|
67
|
-
let encoded = sampleRecord->t2_encode
|
|
68
|
-
expect(encoded) |> toEqual(sampleJson)
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
test(`decode`, _ => {
|
|
72
|
-
let decoded = sampleJson->t2_decode
|
|
73
|
-
expect(decoded) |> toEqual(Belt.Result.Ok(sampleRecord))
|
|
74
|
-
})
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
describe("optional field record: omit array<variant>", _ => {
|
|
78
|
-
open OptionalFieldRecords
|
|
79
|
-
|
|
80
|
-
let sample = Js.Dict.empty()
|
|
81
|
-
sample->Js.Dict.set("a", Js.Json.number(1.))
|
|
82
|
-
|
|
83
|
-
let sampleJson = sample->Js.Json.object_
|
|
84
|
-
|
|
85
|
-
let sampleRecord: t2 = {
|
|
86
|
-
a: 1,
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
test(`encode`, _ => {
|
|
90
|
-
let encoded = sampleRecord->t2_encode
|
|
91
|
-
expect(encoded) |> toEqual(sampleJson)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
test(`decode`, _ => {
|
|
95
|
-
let decoded = sampleJson->t2_decode
|
|
96
|
-
expect(decoded) |> toEqual(Belt.Result.Ok(sampleRecord))
|
|
97
|
-
})
|
|
98
|
-
})
|