Skip to content

Commit

Permalink
Clean up after release
Browse files Browse the repository at this point in the history
  • Loading branch information
DZakh committed Oct 17, 2023
1 parent cf689c7 commit 65862f1
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 53 deletions.
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"ocaml.sandbox": {
"kind": "opam",
"switch": "struct"
}
}
14 changes: 1 addition & 13 deletions CHANGELOG_NEXT.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
2 changes: 1 addition & 1 deletion IDEAS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let trimContract: S.contract<string => string> = S.contract(s => {

- Move S.inline to a separate codegen module

## v5.1
## v5.2

- ppx
- stop reallocate objects without transformations
Expand Down
2 changes: 1 addition & 1 deletion docs/js-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ type Athlete = S.Output<typeof athleteStruct>;
// }
```

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

Expand Down
56 changes: 22 additions & 34 deletions packages/ppx/src/ppx/records.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
45 changes: 45 additions & 0 deletions packages/tests/src/ppx/Ppx_Example_test.bs.mjs
Original file line number Diff line number Diff line change
@@ -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 */
43 changes: 43 additions & 0 deletions packages/tests/src/ppx/Ppx_Example_test.res
Original file line number Diff line number Diff line change
@@ -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<string>,
@as("Rating")
rating: rating,
@as("Age")
deprecatedAgeRestriction: @struct(S.int->S.option->S.deprecate("Use rating instead")) option<int>,
}

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")),
}),
)
})
6 changes: 3 additions & 3 deletions packages/tests/src/ppx/Ppx_Records_test.bs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,22 @@ 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)
};
});

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);
Expand Down
2 changes: 1 addition & 1 deletion packages/tests/src/ppx/Ppx_Records_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down

2 comments on commit 65862f1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 65862f1 Previous: 80ac4f1 Ratio
Parse string 591498829 ops/sec (±0.08%) 706149725 ops/sec (±0.41%) 1.19
Serialize string 588410362 ops/sec (±0.45%) 693539015 ops/sec (±2.10%) 1.18
Advanced object struct factory 267650 ops/sec (±0.43%) 286556 ops/sec (±0.58%) 1.07
Parse advanced object 32079500 ops/sec (±0.18%) 22341504 ops/sec (±0.24%) 0.70
Create and parse advanced object 24377 ops/sec (±0.50%) 58042 ops/sec (±1.08%) 2.38
Parse advanced strict object 15463286 ops/sec (±0.23%) 13141235 ops/sec (±0.73%) 0.85
Serialize advanced object 578255912 ops/sec (±0.11%) 32617508 ops/sec (±0.33%) 0.05640670042989548

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: 65862f1 Previous: 80ac4f1 Ratio
Create and parse advanced object 24377 ops/sec (±0.50%) 58042 ops/sec (±1.08%) 2.38

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.