From 65862f11b00723cb969fe493d9ba42e5668f23b6 Mon Sep 17 00:00:00 2001 From: Dmitry Zakharov Date: Tue, 17 Oct 2023 22:48:21 +0300 Subject: [PATCH] Clean up after release --- .vscode/settings.json | 6 ++ CHANGELOG_NEXT.md | 14 +---- IDEAS.md | 2 +- docs/js-usage.md | 2 +- packages/ppx/src/ppx/records.ml | 56 ++++++++----------- .../tests/src/ppx/Ppx_Example_test.bs.mjs | 45 +++++++++++++++ packages/tests/src/ppx/Ppx_Example_test.res | 43 ++++++++++++++ .../tests/src/ppx/Ppx_Records_test.bs.mjs | 6 +- packages/tests/src/ppx/Ppx_Records_test.res | 2 +- 9 files changed, 123 insertions(+), 53 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 packages/tests/src/ppx/Ppx_Example_test.bs.mjs create mode 100644 packages/tests/src/ppx/Ppx_Example_test.res diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..dbc8bbcc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "ocaml.sandbox": { + "kind": "opam", + "switch": "struct" + } +} diff --git a/CHANGELOG_NEXT.md b/CHANGELOG_NEXT.md index 33545384..c6441b1a 100644 --- a/CHANGELOG_NEXT.md +++ b/CHANGELOG_NEXT.md @@ -1,16 +1,4 @@ -## v5.1 - -- Fixed ts type for S.literal to work with litterally any value -- Added S.undefined to js api -- Added advanced object struct to js api -- Added advanced tuple struct to js api -- Added a proper documentation for JS/TS users -- Added table of contents and splitted documentation into multiple documents -- Improved tree-shaking. For the JS/TS api example bundle-size reduced by 13% - 2 kB (0.55 kB minified + gzipped) -- Added `S.Error.reason` -- Added alpha version of `S.merge` helper (JS/TS api only) -- Added `S.name`/`S.setName` for JS/TS api -- Documented `S.classify`/`S.name`/`S.setName` +## v5.2 ## Opt-in ppx support diff --git a/IDEAS.md b/IDEAS.md index 6d9dd225..461c5293 100644 --- a/IDEAS.md +++ b/IDEAS.md @@ -30,7 +30,7 @@ let trimContract: S.contract string> = S.contract(s => { - Move S.inline to a separate codegen module -## v5.1 +## v5.2 - ppx - stop reallocate objects without transformations diff --git a/docs/js-usage.md b/docs/js-usage.md index 7cd8ae37..a3b5e7a2 100644 --- a/docs/js-usage.md +++ b/docs/js-usage.md @@ -370,7 +370,7 @@ type Athlete = S.Output; // } ``` -That looks much better than before. And the same as for advanced objects, you can use the same struct for transforming the parsed data back to the initial format. Also, it has 0 performance overhead and as fast as parsing tuples without the transformation. +That looks much better than before. And the same as for advanced objects, you can use the same struct for transforming the parsed data back to the initial format. Also, it has 0 performance overhead and is as fast as parsing tuples without the transformation. ## Unions diff --git a/packages/ppx/src/ppx/records.ml b/packages/ppx/src/ppx/records.ml index 2c0758ed..a26d4ab2 100644 --- a/packages/ppx/src/ppx/records.ml +++ b/packages/ppx/src/ppx/records.ml @@ -3,47 +3,42 @@ open Parsetree open Ast_helper open Utils -(* TODO: Support recursive types *) -(* TODO: Move default from here *) -(* TODO: check optional *) - -type parsed_decl = { +type field = { name : string; - (* "NAME" *) - key : expression; - (* v.NAME *) - field : expression; + maybe_alias : expression option; struct_expr : expression; - default : expression option; - is_optional : bool; } -let generate_decoder decls = +let generate_decoder fields = (* Use Obj.magic to cast to uncurried function in case of uncurried mode *) [%expr S.Object.factory (Obj.magic (fun (s : S.Object.ctx) -> [%e Exp.record - (decls - |> List.map (fun decl -> - ( lid decl.name, - [%expr s.field [%e decl.key] [%e decl.struct_expr]] ))) + (fields + |> List.map (fun field -> + let original_field_name_expr = + match field.maybe_alias with + | Some alias -> alias + | None -> + Exp.constant + (Pconst_string (field.name, Location.none, None)) + in + + ( lid field.name, + [%expr + s.field [%e original_field_name_expr] + [%e field.struct_expr]] ))) None]))] let parse_decl { pld_name = { txt }; pld_loc; pld_type; pld_attributes } = - let default = - match get_attribute_by_name pld_attributes "struct.default" with + let maybe_alias = + match get_attribute_by_name pld_attributes "as" with | Ok (Some attribute) -> Some (get_expr_from_payload attribute) | Ok None -> None | Error s -> fail pld_loc s in - let key = - match get_attribute_by_name pld_attributes "struct.field" with - | Ok (Some attribute) -> get_expr_from_payload attribute - | Ok None -> Exp.constant (Pconst_string (txt, Location.none, None)) - | Error s -> fail pld_loc s - in let optional_attrs = [ "ns.optional"; "res.optional" ] in let is_optional = optional_attrs @@ -56,15 +51,8 @@ let parse_decl { pld_name = { txt }; pld_loc; pld_type; pld_attributes } = else struct_expr in - { - name = txt; - key; - field = Exp.field [%expr v] (lid txt); - struct_expr; - default; - is_optional; - } + { name = txt; maybe_alias; struct_expr } let generate_struct_expr decls = - let parsed_decls = List.map parse_decl decls in - generate_decoder parsed_decls + let fields = List.map parse_decl decls in + generate_decoder fields diff --git a/packages/tests/src/ppx/Ppx_Example_test.bs.mjs b/packages/tests/src/ppx/Ppx_Example_test.bs.mjs new file mode 100644 index 00000000..cfb523a5 --- /dev/null +++ b/packages/tests/src/ppx/Ppx_Example_test.bs.mjs @@ -0,0 +1,45 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as U from "../utils/U.bs.mjs"; +import Ava from "ava"; +import * as S$RescriptStruct from "rescript-struct/src/S.bs.mjs"; + +var ratingStruct = S$RescriptStruct.union([ + S$RescriptStruct.literal("G"), + S$RescriptStruct.literal("PG"), + S$RescriptStruct.literal("PG13"), + S$RescriptStruct.literal("R") + ]); + +var filmStruct = S$RescriptStruct.$$Object.factory(function (s) { + return { + Id: s.f("Id", S$RescriptStruct.$$float), + Title: s.f("Title", S$RescriptStruct.string), + Tags: s.f("Tags", S$RescriptStruct.$$Option.getOr(S$RescriptStruct.option(S$RescriptStruct.array(S$RescriptStruct.string)), [])), + Rating: s.f("Rating", ratingStruct), + Age: s.f("Age", S$RescriptStruct.deprecate(S$RescriptStruct.option(S$RescriptStruct.$$int), "Use rating instead")) + }; + }); + +Ava("Example", (function (t) { + U.assertEqualStructs(t, filmStruct, S$RescriptStruct.object(function (s) { + return { + Id: s.f("Id", S$RescriptStruct.$$float), + Title: s.f("Title", S$RescriptStruct.string), + Tags: s.o("Tags", S$RescriptStruct.array(S$RescriptStruct.string), []), + Rating: s.f("Rating", S$RescriptStruct.union([ + S$RescriptStruct.literal("G"), + S$RescriptStruct.literal("PG"), + S$RescriptStruct.literal("PG13"), + S$RescriptStruct.literal("R") + ])), + Age: s.f("Age", S$RescriptStruct.deprecate(S$RescriptStruct.option(S$RescriptStruct.$$int), "Use rating instead")) + }; + }), undefined); + })); + +export { + ratingStruct , + filmStruct , +} +/* ratingStruct Not a pure module */ diff --git a/packages/tests/src/ppx/Ppx_Example_test.res b/packages/tests/src/ppx/Ppx_Example_test.res new file mode 100644 index 00000000..4e01489e --- /dev/null +++ b/packages/tests/src/ppx/Ppx_Example_test.res @@ -0,0 +1,43 @@ +open Ava +open U + +@struct +type rating = + | @as("G") GeneralAudiences + | @as("PG") ParentalGuidanceSuggested + | @as("PG13") ParentalStronglyCautioned + | @as("R") Restricted +@struct +type film = { + @as("Id") + id: float, + @as("Title") + title: string, + @as("Tags") + tags: @struct(S.array(S.string)->S.option->S.Option.getOr([])) array, + @as("Rating") + rating: rating, + @as("Age") + deprecatedAgeRestriction: @struct(S.int->S.option->S.deprecate("Use rating instead")) option, +} + +test("Example", t => { + t->assertEqualStructs( + filmStruct, + S.object(s => { + id: s.field("Id", S.float), + title: s.field("Title", S.string), + tags: s.fieldOr("Tags", S.array(S.string), []), + rating: s.field( + "Rating", + S.union([ + S.literal(GeneralAudiences), + S.literal(ParentalGuidanceSuggested), + S.literal(ParentalStronglyCautioned), + S.literal(Restricted), + ]), + ), + deprecatedAgeRestriction: s.field("Age", S.option(S.int)->S.deprecate("Use rating instead")), + }), + ) +}) diff --git a/packages/tests/src/ppx/Ppx_Records_test.bs.mjs b/packages/tests/src/ppx/Ppx_Records_test.bs.mjs index cce16879..9450ae35 100644 --- a/packages/tests/src/ppx/Ppx_Records_test.bs.mjs +++ b/packages/tests/src/ppx/Ppx_Records_test.bs.mjs @@ -29,7 +29,7 @@ Ava("Simple record struct", (function (t) { var recordWithAliasStruct = S$RescriptStruct.$$Object.factory(function (s) { return { - label: s.f("aliased-label", S$RescriptStruct.string), + "aliased-label": s.f("aliased-label", S$RescriptStruct.string), value: s.f("value", S$RescriptStruct.$$int) }; }); @@ -37,14 +37,14 @@ var recordWithAliasStruct = S$RescriptStruct.$$Object.factory(function (s) { Ava("Record struct with alias for field name", (function (t) { U.assertEqualStructs(t, recordWithAliasStruct, S$RescriptStruct.object(function (s) { return { - label: s.f("aliased-label", S$RescriptStruct.string), + "aliased-label": s.f("aliased-label", S$RescriptStruct.string), value: s.f("value", S$RescriptStruct.$$int) }; }), undefined); t.deepEqual(S$RescriptStruct.parseWith({"aliased-label":"foo",value:1}, recordWithAliasStruct), { TAG: "Ok", _0: { - label: "foo", + "aliased-label": "foo", value: 1 } }, undefined); diff --git a/packages/tests/src/ppx/Ppx_Records_test.res b/packages/tests/src/ppx/Ppx_Records_test.res index 039d9b5c..776963cb 100644 --- a/packages/tests/src/ppx/Ppx_Records_test.res +++ b/packages/tests/src/ppx/Ppx_Records_test.res @@ -23,7 +23,7 @@ test("Simple record struct", t => { @struct type recordWithAlias = { - @struct.field("aliased-label") label: string, + @as("aliased-label") label: string, value: int, } test("Record struct with alias for field name", t => {