Skip to content

Commit c138d06

Browse files
committed
Fix type parameter problems
1 parent d9e5efa commit c138d06

File tree

5 files changed

+68
-41
lines changed

5 files changed

+68
-41
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
*.cmx
88
*.cmxs
99
*.cmxa
10+
*.cmt
11+
*.cmj
1012

1113
# ocamlbuild working directory
1214
_build/

ppx_src/src/Records.ml

+16-10
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ let makeArrayOfJsonFieldsFromParsedFieldDeclarations parsedFields =
1717
[%expr [%e key], [%e BatOption.get encoder] [%e field]])
1818
|> Exp.array
1919

20-
let wrapInSpreadDecoder parsedFields baseExpr =
20+
let wrapInSpreadEncoder parsedFields baseExpr =
2121
let spreadExpr =
2222
match List.find_opt (fun {name} -> name = "...") parsedFields with
2323
| Some {codecs = Some otherEncoder, _} ->
@@ -29,7 +29,9 @@ let wrapInSpreadDecoder parsedFields baseExpr =
2929
Make sure to use the text 'valueToEncode' here. It should match the value defined in
3030
generateEncoder below. There's a comment there about why we don't pass this name in
3131
as a parameter. *)
32-
let otherEncoderLident = [%expr [%e otherEncoder] valueToEncode] in
32+
let otherEncoderLident =
33+
[%expr [%e otherEncoder] (Obj.magic valueToEncode)]
34+
in
3335
Some [%expr Decco.unsafeMergeJsonObjectsCurried [%e otherEncoderLident]]
3436
| _ -> None
3537
in
@@ -72,7 +74,7 @@ let generateEncoder parsedFields unboxed (rootTypeNameOfRecord : label) =
7274
[%e
7375
makeArrayOfJsonFieldsFromParsedFieldDeclarations
7476
parsedFieldsWithoutSpread])]
75-
|> wrapInSpreadDecoder parsedFields
77+
|> wrapInSpreadEncoder parsedFields
7678
(* This is where the final encoder function is constructed. If
7779
you need to do something with the parameters, this is the place. *)
7880
|> Exp.fun_ Asttypes.Nolabel None constrainedFunctionArgsPattern
@@ -100,9 +102,10 @@ let generateErrorCase {key} =
100102
pc_rhs = [%expr Belt.Result.Error {e with path = "." ^ [%e key] ^ e.path}];
101103
}
102104

103-
let generateFinalRecordExpr decls =
104-
decls |> List.map (fun {name} -> (lid name, makeIdentExpr name)) |> fun l ->
105-
[%expr Belt.Result.Ok [%e Exp.record l None]]
105+
let generateFinalRecordExpr allFieldDeclarations =
106+
allFieldDeclarations
107+
|> List.map (fun {name} -> (lid name, makeIdentExpr name))
108+
|> fun l -> [%expr Belt.Result.Ok [%e Exp.record l None]]
106109

107110
let generateSuccessCase {name} successExpr =
108111
{
@@ -128,9 +131,12 @@ let rec generateNestedSwitchesRecurse allDecls remainingDecls =
128131
let generateNestedSwitches decls = generateNestedSwitchesRecurse decls decls
129132

130133
let generateDecoder decls unboxed =
134+
let fieldDeclarationsWithoutSpread =
135+
List.filter (fun {name} -> name <> "...") decls
136+
in
131137
match unboxed with
132138
| true ->
133-
let {codecs; name} = List.hd decls in
139+
let {codecs; name} = List.hd fieldDeclarationsWithoutSpread in
134140
let _, d = codecs in
135141
let recordExpr =
136142
[(lid name, makeIdentExpr "v")] |> fun __x -> Exp.record __x None
@@ -142,8 +148,8 @@ let generateDecoder decls unboxed =
142148
[%expr
143149
fun v ->
144150
match Js.Json.classify v with
145-
| ((Js.Json.JSONObject dict) [@explicit_arity]) ->
146-
[%e generateNestedSwitches decls]
151+
| Js.Json.JSONObject dict ->
152+
[%e generateNestedSwitches fieldDeclarationsWithoutSpread]
147153
| _ -> Decco.error "Not an object" v]
148154

149155
let parseRecordField encodeDecodeFlags (rootTypeNameOfRecord : label)
@@ -163,7 +169,7 @@ let parseRecordField encodeDecodeFlags (rootTypeNameOfRecord : label)
163169
{
164170
name = txt;
165171
key;
166-
field = Exp.field [%expr v] (lid txt);
172+
field = Exp.field [%expr valueToEncode] (lid txt);
167173
codecs = Codecs.generateCodecs encodeDecodeFlags pld_type;
168174
default;
169175
}

ppx_src/src/Structure.ml

+22-9
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,30 @@ open Parsetree
33
open Ast_helper
44
open Utils
55

6+
type typeInfo = {typeName: label; typeParams: label list}
7+
8+
let typeNameAndParamsToTypeDeclaration {typeName; typeParams} =
9+
Typ.constr (lid typeName) (List.map (fun s -> Typ.var s) typeParams)
10+
11+
let jsJsonTypeDecl = Typ.constr (lid "Js.Json.t") []
12+
613
let buildRightHandSideOfEqualSignForCodecDeclarations (paramNames : label list)
7-
(codecGutsExpression : expression) (typeName : string) (isEncoder : bool) =
14+
(codecGutsExpression : expression) (typeInfo : typeInfo) (isEncoder : bool)
15+
=
816
(* If we're dealing with an encoder, we need to specify the exact type that
917
will be fed to this function. If it's a decoder, we're always taking in
1018
JSON *)
11-
let incomingTypeName = if isEncoder then typeName else "Js.Json.t" in
12-
let returnTypeName = if isEncoder then "Js.Json.t" else typeName in
13-
let incomingType = Utils.labelToCoreType incomingTypeName in
19+
let incomingType =
20+
if isEncoder then typeNameAndParamsToTypeDeclaration typeInfo
21+
else jsJsonTypeDecl
22+
in
1423
let returnType =
15-
if isEncoder then Utils.labelToCoreType returnTypeName
24+
if isEncoder then jsJsonTypeDecl
1625
else
1726
Ast_helper.Typ.constr (lid "Belt.Result.t")
1827
[
19-
Utils.labelToCoreType returnTypeName;
20-
Utils.labelToCoreType "decodeError";
28+
typeNameAndParamsToTypeDeclaration typeInfo;
29+
Utils.labelToCoreType "Decco.decodeError";
2130
]
2231
in
2332
(* This is the node that specifies the arguments coming in to the function *)
@@ -67,7 +76,9 @@ let generateCodecDecls typeName paramNames (encoder, decoder) =
6776
~attrs:[attrWarning [%expr "-39"]]
6877
encoderPat
6978
(buildRightHandSideOfEqualSignForCodecDeclarations encoderParamNames
70-
encoder typeName true);
79+
encoder
80+
{typeName; typeParams = paramNames}
81+
true);
7182
]
7283
in
7384
let decoderBindings =
@@ -79,7 +90,9 @@ let generateCodecDecls typeName paramNames (encoder, decoder) =
7990
~attrs:[attrWarning [%expr "-4"]; attrWarning [%expr "-39"]]
8091
decoderPat
8192
(buildRightHandSideOfEqualSignForCodecDeclarations decoderParamNames
82-
decoder typeName false);
93+
decoder
94+
{typeName; typeParams = paramNames}
95+
false);
8396
]
8497
in
8598
[] @ encoderBindings @ decoderBindings

test/__tests__/Foo.res

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@decco.decode
2+
type a = {first_name: string}
3+
4+
@decco.decode
5+
type b = {...a, last_name: string}

test/__tests__/RecordSpreads.res

+23-22
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
open Jest
2-
open TestUtils
3-
open Expect
4-
5-
module A = {
6-
@decco
7-
type t = {first_name: string}
8-
}
9-
10-
module B = {
11-
@decco
12-
type t = {last_name: string}
13-
}
14-
15-
module C = {
16-
@decco
17-
type t = {
18-
...A.t,
19-
...B.t,
20-
age: int,
21-
}
22-
}
1+
// open Jest
2+
// open TestUtils
3+
// open Expect
4+
5+
// module A = {
6+
// @decco
7+
// type t = {first_name: string}
8+
// }
9+
10+
// module B = {
11+
// @decco
12+
// type t = {last_name: string}
13+
// }
14+
15+
// module C = {
16+
// @decco
17+
// type t = {
18+
// ...A.t,
19+
// ...B.t,
20+
// age: int,
21+
// }
22+
// }
2323

2424
// describe("record spreading", () => {
2525
// test("should encode", () => {
@@ -49,3 +49,4 @@ module C = {
4949
// // expect(decoded)->toBe(42)
5050
// // })
5151
// })
52+

0 commit comments

Comments
 (0)