Skip to content

Commit

Permalink
Fix @spice.encode, @spice.decode (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
mununki authored Feb 17, 2023
1 parent cd77144 commit 50e9d06
Show file tree
Hide file tree
Showing 15 changed files with 250 additions and 86 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

# 0.1.12

- Fix build error where `@spice.encode`, `@spice.decode` are used

# 0.1.11

- Build the executable with static linking for Linux with musl
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@greenlabs/ppx-spice",
"version": "0.1.11",
"version": "0.1.12",
"description": "ReScript PPX which generate JSON (de)serializer",
"license": "MIT",
"author": "Greenlabs Dev <developer@greenlabs.co.kr>",
Expand Down
116 changes: 66 additions & 50 deletions src/ppx/codecs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -28,51 +28,57 @@ and generate_constr_codecs { do_encode; do_decode }
let open Longident in
match identifier with
| Lident "string" ->
( some_if_true do_encode [%expr Spice.stringToJson],
some_if_true do_decode [%expr Spice.stringFromJson] )
( (if do_encode then Some [%expr Spice.stringToJson] else None),
if do_decode then Some [%expr Spice.stringFromJson] else None )
| Lident "int" ->
( some_if_true do_encode [%expr Spice.intToJson],
some_if_true do_decode [%expr Spice.intFromJson] )
( (if do_encode then Some [%expr Spice.intToJson] else None),
if do_decode then Some [%expr Spice.intFromJson] else None )
| Lident "int64" ->
( some_if_true do_encode [%expr Spice.int64ToJson],
some_if_true do_decode [%expr Spice.int64FromJson] )
( (if do_encode then Some [%expr Spice.int64ToJson] else None),
if do_decode then Some [%expr Spice.int64FromJson] else None )
| Lident "float" ->
( some_if_true do_encode [%expr Spice.floatToJson],
some_if_true do_decode [%expr Spice.floatFromJson] )
( (if do_encode then Some [%expr Spice.floatToJson] else None),
if do_decode then Some [%expr Spice.floatFromJson] else None )
| Lident "bool" ->
( some_if_true do_encode [%expr Spice.boolToJson],
some_if_true do_decode [%expr Spice.boolFromJson] )
( (if do_encode then Some [%expr Spice.boolToJson] else None),
if do_decode then Some [%expr Spice.boolFromJson] else None )
| Lident "unit" ->
( some_if_true do_encode [%expr Spice.unitToJson],
some_if_true do_decode [%expr Spice.unitFromJson] )
( (if do_encode then Some [%expr Spice.unitToJson] else None),
if do_decode then Some [%expr Spice.unitFromJson] else None )
| Lident "array" ->
( some_if_true do_encode [%expr Spice.arrayToJson],
some_if_true do_decode [%expr Spice.arrayFromJson] )
( (if do_encode then Some [%expr Spice.arrayToJson] else None),
if do_decode then Some [%expr Spice.arrayFromJson] else None )
| Lident "list" ->
( some_if_true do_encode [%expr Spice.listToJson],
some_if_true do_decode [%expr Spice.listFromJson] )
( (if do_encode then Some [%expr Spice.listToJson] else None),
if do_decode then Some [%expr Spice.listFromJson] else None )
| Lident "option" ->
( some_if_true do_encode [%expr Spice.optionToJson],
some_if_true do_decode [%expr Spice.optionFromJson] )
( (if do_encode then Some [%expr Spice.optionToJson] else None),
if do_decode then Some [%expr Spice.optionFromJson] else None )
| Ldot (Ldot (Lident "Belt", "Result"), "t") ->
( some_if_true do_encode [%expr Spice.resultToJson],
some_if_true do_decode [%expr Spice.resultFromJson] )
( (if do_encode then Some [%expr Spice.resultToJson] else None),
if do_decode then Some [%expr Spice.resultFromJson] else None )
| Ldot (Ldot (Lident "Js", "Dict"), "t") ->
( some_if_true do_encode [%expr Spice.dictToJson],
some_if_true do_decode [%expr Spice.dictFromJson] )
( (if do_encode then Some [%expr Spice.dictToJson] else None),
if do_decode then Some [%expr Spice.dictFromJson] else None )
| Ldot (Ldot (Lident "Js", "Json"), "t") ->
( some_if_true do_encode [%expr fun v -> v],
some_if_true do_decode [%expr fun v -> Belt.Result.Ok v] )
( (if do_encode then Some [%expr fun v -> v] else None),
if do_decode then Some [%expr fun v -> Belt.Result.Ok v] else None )
| Lident s ->
( some_if_true do_encode (make_ident_expr (s ^ Utils.encoder_func_suffix)),
some_if_true do_decode (make_ident_expr (s ^ Utils.decoder_func_suffix))
)
( (if do_encode then Some (make_ident_expr (s ^ Utils.encoder_func_suffix))
else None),
if do_decode then Some (make_ident_expr (s ^ Utils.decoder_func_suffix))
else None )
| Ldot (left, right) ->
( some_if_true do_encode
(Exp.ident (mknoloc (Ldot (left, right ^ Utils.encoder_func_suffix)))),
some_if_true do_decode
(Exp.ident (mknoloc (Ldot (left, right ^ Utils.decoder_func_suffix))))
)
( (if do_encode then
Some
(Exp.ident
(mknoloc (Ldot (left, right ^ Utils.encoder_func_suffix))))
else None),
if do_decode then
Some
(Exp.ident
(mknoloc (Ldot (left, right ^ Utils.decoder_func_suffix))))
else None )
| Lapply (_, _) -> fail loc "Lapply syntax not yet handled by spice"

and generate_codecs ({ do_encode; do_decode } as generator_settings)
Expand All @@ -86,32 +92,42 @@ and generate_codecs ({ do_encode; do_decode } as generator_settings)
let composite_codecs =
List.map (generate_codecs generator_settings) types
in
( some_if_true do_encode
(composite_codecs
|> List.map (fun (e, _) -> Option.get e)
|> Tuple.generate_encoder),
some_if_true do_decode
(composite_codecs
|> List.map (fun (_, d) -> Option.get d)
|> Tuple.generate_decoder) )
( (if do_encode then
Some
(composite_codecs
|> List.map (fun (e, _) -> Option.get e)
|> Tuple.generate_encoder)
else None),
if do_decode then
Some
(composite_codecs
|> List.map (fun (_, d) -> Option.get d)
|> Tuple.generate_decoder)
else None )
| Ptyp_var s ->
( some_if_true do_encode (make_ident_expr (encoder_var_prefix ^ s)),
some_if_true do_decode (make_ident_expr (decoder_var_prefix ^ s)) )
( (if do_encode then Some (make_ident_expr (encoder_var_prefix ^ s))
else None),
if do_decode then Some (make_ident_expr (decoder_var_prefix ^ s))
else None )
| Ptyp_constr (constr, typeArgs) -> (
let custom_codec = get_attribute_by_name ptyp_attributes "spice.codec" in
let encode, decode =
match custom_codec with
| Ok None -> generate_constr_codecs generator_settings constr
| Ok (Some attribute) ->
let expr = get_expression_from_payload attribute in
( some_if_true do_encode
[%expr
let e, _ = [%e expr] in
e],
some_if_true do_decode
[%expr
let _, d = [%e expr] in
d] )
( (if do_encode then
Some
[%expr
let e, _ = [%e expr] in
e]
else None),
if do_decode then
Some
[%expr
let _, d = [%e expr] in
d]
else None )
| Error s -> fail ptyp_loc s
in
match List.length typeArgs = 0 with
Expand Down
14 changes: 8 additions & 6 deletions src/ppx/polyvariants.ml
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,14 @@ let generate_codecs ({ do_encode; do_decode } as generator_settings) row_fields
in

let encoder =
some_if_true do_encode
(List.map
(generate_encoder_case generator_settings unboxed has_attr_as)
parsed_fields
|> Exp.match_ [%expr v]
|> Exp.fun_ Asttypes.Nolabel None [%pat? v])
if do_encode then
Some
(List.map
(generate_encoder_case generator_settings unboxed has_attr_as)
parsed_fields
|> Exp.match_ [%expr v]
|> Exp.fun_ Asttypes.Nolabel None [%pat? v])
else None
in

let decoder =
Expand Down
8 changes: 5 additions & 3 deletions src/ppx/records.ml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ let parse_decl generator_settings
| Some encode, Some decode ->
( Some [%expr Spice.optionToJson [%e encode]],
Some [%expr Spice.optionFromJson [%e decode]] )
| _ -> codecs
| Some encode, _ -> (Some [%expr Spice.optionToJson [%e encode]], None)
| _, Some decode -> (None, Some [%expr Spice.optionFromJson [%e decode]])
| None, None -> codecs
else codecs
in

Expand All @@ -159,5 +161,5 @@ let parse_decl generator_settings
let generate_codecs ({ do_encode; do_decode } as generator_settings) decls
unboxed =
let parsed_decls = List.map (parse_decl generator_settings) decls in
( some_if_true do_encode (generate_encoder parsed_decls unboxed),
some_if_true do_decode (generate_decoder parsed_decls unboxed) )
( (if do_encode then Some (generate_encoder parsed_decls unboxed) else None),
if do_decode then Some (generate_decoder parsed_decls unboxed) else None )
13 changes: 0 additions & 13 deletions src/ppx/utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,16 @@ open Parsetree
open Ast_helper

let annotation_name = "spice"

let encoder_func_suffix = "_encode"

let decoder_func_suffix = "_decode"

let encoder_var_prefix = "encoder_"

let decoder_var_prefix = "decoder_"

let loc = !default_loc

let fail loc message = Location.raise_errorf ~loc "%s" message

let longident_parse = Longident.parse [@@ocaml.warning "-3"]

let mkloc txt loc = { Location.txt; loc }

let mknoloc txt = mkloc txt Location.none

let lid ?(loc = Location.none) s = mkloc (Longident.parse s) loc

let make_ident_expr ?attrs s = Exp.ident ?attrs (mknoloc (longident_parse s))

let tuple_or_singleton tuple l =
Expand Down Expand Up @@ -113,5 +102,3 @@ let attr_warning expr =
attr_payload = PStr [ { pstr_desc = Pstr_eval (expr, []); pstr_loc = loc } ];
attr_loc = loc;
}

let some_if_true cond a = match cond with true -> Some a | false -> None
13 changes: 8 additions & 5 deletions src/ppx/variants.ml
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,14 @@ let generate_codecs ({ do_encode; do_decode } as generator_settings)
in

let encoder =
some_if_true do_encode
(parsed_decls
|> List.map (generate_encoder_case generator_settings unboxed has_attr_as)
|> Exp.match_ [%expr v]
|> Exp.fun_ Asttypes.Nolabel None [%pat? v])
if do_encode then
Some
(parsed_decls
|> List.map
(generate_encoder_case generator_settings unboxed has_attr_as)
|> Exp.match_ [%expr v]
|> Exp.fun_ Asttypes.Nolabel None [%pat? v])
else None
in

let decoder =
Expand Down
2 changes: 1 addition & 1 deletion src/ppx_spice.opam
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
opam-version: "2.0"
name: "ppx_spice"
version: "0.1.11"
version: "0.1.12"
synopsis: "ReScript PPX which generate JSON (de)serializer"
description: """
ReScript PPX which generate JSON (de)serializer
Expand Down
36 changes: 36 additions & 0 deletions test/__tests__/test_encode_decode.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 41 additions & 0 deletions test/__tests__/test_encode_decode.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
open Jest
open Expect
// open Belt

describe("encode only", _ => {
open EncodeDecode

let sample = Js.Dict.empty()
sample->Js.Dict.set("name", Js.Json.string("Alice"))
sample->Js.Dict.set("nickname", Js.Json.string("Ecila"))
let sampleJson = sample->Js.Json.object_

let sampleRecord: te = {
name: "Alice",
nickname: "Ecila",
}

test(`encode`, _ => {
let encoded = sampleRecord->te_encode
expect(encoded) |> toEqual(sampleJson)
})
})

describe("decode only", _ => {
open EncodeDecode

let sample = Js.Dict.empty()
sample->Js.Dict.set("name", Js.Json.string("Alice"))
sample->Js.Dict.set("nickname", Js.Json.string("Ecila"))
let sampleJson = sample->Js.Json.object_

let sampleRecord: td = {
name: "Alice",
nickname: "Ecila",
}

test(`decode`, _ => {
let decoded = sampleJson->td_decode
expect(decoded) |> toEqual(Belt.Result.Ok(sampleRecord))
})
})
2 changes: 1 addition & 1 deletion test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"devDependencies": {
"@glennsl/bs-jest": "^0.7.0",
"jest": "^27.3.1",
"rescript": "^10.0.1"
"rescript": "^10.1.2"
},
"scripts": {
"res:build": "rescript",
Expand Down
Loading

0 comments on commit 50e9d06

Please sign in to comment.