From 2349fd9055e9929661ddc7bf0fbb631a6ca80d2f Mon Sep 17 00:00:00 2001 From: Dmitry Zakharov Date: Fri, 8 Dec 2023 23:53:02 +0400 Subject: [PATCH] Add @s.defaultWith --- packages/rescript-schema-ppx/README.md | 22 +++++++++++++--- .../rescript-schema-ppx/src/ppx/Structure.ml | 9 +++++++ .../tests/src/ppx/Ppx_Example_test.bs.mjs | 23 +++++++++++----- packages/tests/src/ppx/Ppx_Example_test.res | 17 +++++++++--- packages/tests/src/utils/U.bs.mjs | 26 +++++++------------ packages/tests/src/utils/U.res | 6 +++-- 6 files changed, 70 insertions(+), 33 deletions(-) diff --git a/packages/rescript-schema-ppx/README.md b/packages/rescript-schema-ppx/README.md index b2fd3636..3846ec42 100644 --- a/packages/rescript-schema-ppx/README.md +++ b/packages/rescript-schema-ppx/README.md @@ -124,10 +124,10 @@ Specifies custom schema for the type. ```rescript @schema -type url = @s.matches(S.string->S.String.url) string +type t = @s.matches(S.string->S.String.url) string // Generated by PPX ⬇️ -let urlSchema = S.string->S.String.url +let schema = S.string->S.String.url ``` ### `@s.default('value)` @@ -138,8 +138,22 @@ Wraps the type expression schema into an option with the provided default value. ```rescript @schema -type stringWithDefault = @s.default("Unknown") string +type t = @s.default("Unknown") string // Generated by PPX ⬇️ -let stringWithDefaultSchema = S.option(S.string)->S.Option.getOr("Unknown") +let schema = S.option(S.string)->S.Option.getOr("Unknown") +``` + +### `@s.defaultWith(unit => 'value)` + +**Applies to**: type expressions + +Wraps the type expression schema into an option with callback to get the default value. + +```rescript +@schema +type t = @s.defaultWith(() => []) array + +// Generated by PPX ⬇️ +let schema = S.option(S.array(S.string))->S.Option.getOrWith(() => []) ``` diff --git a/packages/rescript-schema-ppx/src/ppx/Structure.ml b/packages/rescript-schema-ppx/src/ppx/Structure.ml index 426ed0c4..bb17c6f7 100644 --- a/packages/rescript-schema-ppx/src/ppx/Structure.ml +++ b/packages/rescript-schema-ppx/src/ppx/Structure.ml @@ -173,6 +173,15 @@ and generateCoreTypeSchemaExpression {ptyp_desc; ptyp_loc; ptyp_attributes} = S.Option.getOr (S.option [%e schema_expression]) [%e default_value]] | Error s -> fail ptyp_loc s in + let schema_expression = + match getAttributeByName ptyp_attributes "s.defaultWith" with + | Ok None -> schema_expression + | Ok (Some attribute) -> + let default_cb = getExpressionFromPayload attribute in + [%expr + S.Option.getOrWith (S.option [%e schema_expression]) [%e default_cb]] + | Error s -> fail ptyp_loc s + in schema_expression let generateTypeDeclarationSchemaExpression type_declaration = diff --git a/packages/tests/src/ppx/Ppx_Example_test.bs.mjs b/packages/tests/src/ppx/Ppx_Example_test.bs.mjs index d6ab545c..a3cc1b60 100644 --- a/packages/tests/src/ppx/Ppx_Example_test.bs.mjs +++ b/packages/tests/src/ppx/Ppx_Example_test.bs.mjs @@ -38,22 +38,33 @@ Ava("Main example", (function (t) { }), undefined); })); -var urlSchema = S$RescriptSchema.$$String.url(S$RescriptSchema.string, undefined); +var matchesSchema = S$RescriptSchema.$$String.url(S$RescriptSchema.string, undefined); Ava("@s.matches", (function (t) { - U.assertEqualSchemas(t, urlSchema, S$RescriptSchema.$$String.url(S$RescriptSchema.string, undefined), undefined); + U.assertEqualSchemas(t, matchesSchema, S$RescriptSchema.$$String.url(S$RescriptSchema.string, undefined), undefined); })); -var stringWithDefaultSchema = S$RescriptSchema.$$Option.getOr(S$RescriptSchema.option(S$RescriptSchema.string), "Unknown"); +var defaultSchema = S$RescriptSchema.$$Option.getOr(S$RescriptSchema.option(S$RescriptSchema.string), "Unknown"); Ava("@s.default", (function (t) { - U.assertEqualSchemas(t, stringWithDefaultSchema, S$RescriptSchema.$$Option.getOr(S$RescriptSchema.option(S$RescriptSchema.string), "Unknown"), undefined); + U.assertEqualSchemas(t, defaultSchema, S$RescriptSchema.$$Option.getOr(S$RescriptSchema.option(S$RescriptSchema.string), "Unknown"), undefined); + })); + +var defaultWithSchema = S$RescriptSchema.$$Option.getOrWith(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string)), (function () { + return []; + })); + +Ava("@s.defaultWith", (function (t) { + U.assertEqualSchemas(t, defaultWithSchema, S$RescriptSchema.$$Option.getOrWith(S$RescriptSchema.option(S$RescriptSchema.array(S$RescriptSchema.string)), (function () { + return []; + })), undefined); })); export { ratingSchema , filmSchema , - urlSchema , - stringWithDefaultSchema , + matchesSchema , + defaultSchema , + defaultWithSchema , } /* ratingSchema Not a pure module */ diff --git a/packages/tests/src/ppx/Ppx_Example_test.res b/packages/tests/src/ppx/Ppx_Example_test.res index 8728a6dc..0c3d7f26 100644 --- a/packages/tests/src/ppx/Ppx_Example_test.res +++ b/packages/tests/src/ppx/Ppx_Example_test.res @@ -44,13 +44,22 @@ test("Main example", t => { }) @schema -type url = @s.matches(S.string->S.String.url) string +type matches = @s.matches(S.string->S.String.url) string test("@s.matches", t => { - t->assertEqualSchemas(urlSchema, S.string->S.String.url) + t->assertEqualSchemas(matchesSchema, S.string->S.String.url) }) @schema -type stringWithDefault = @s.default("Unknown") string +type default = @s.default("Unknown") string test("@s.default", t => { - t->assertEqualSchemas(stringWithDefaultSchema, S.option(S.string)->S.Option.getOr("Unknown")) + t->assertEqualSchemas(defaultSchema, S.option(S.string)->S.Option.getOr("Unknown")) +}) + +@schema +type defaultWith = @s.defaultWith(() => []) array +test("@s.defaultWith", t => { + t->assertEqualSchemas( + defaultWithSchema, + S.option(S.array(S.string))->S.Option.getOrWith(() => []), + ) }) diff --git a/packages/tests/src/utils/U.bs.mjs b/packages/tests/src/utils/U.bs.mjs index e15c5036..10cf621d 100644 --- a/packages/tests/src/utils/U.bs.mjs +++ b/packages/tests/src/utils/U.bs.mjs @@ -43,23 +43,15 @@ function cleanUpSchema(schema) { Object.entries(schema).forEach(function (param) { var value = param[1]; var key = param[0]; - switch (key) { - case "f" : - case "i" : - case "n" : - case "op" : - case "opa" : - case "os" : - case "p" : - case "s" : - return ; - default: - if (typeof value === "object" && value !== null) { - $$new[key] = cleanUpSchema(value); - } else { - $$new[key] = value; - } - return ; + if (key === "i" || typeof value === "function") { + return ; + } else { + if (typeof value === "object" && value !== null) { + $$new[key] = cleanUpSchema(value); + } else { + $$new[key] = value; + } + return ; } }); return $$new; diff --git a/packages/tests/src/utils/U.res b/packages/tests/src/utils/U.res index a15c8903..2b9411f2 100644 --- a/packages/tests/src/utils/U.res +++ b/packages/tests/src/utils/U.res @@ -36,9 +36,11 @@ let rec cleanUpSchema = schema => { ->Dict.toArray ->Array.forEach(((key, value)) => { switch key { - | "s" | "p" | "i" | "f" | "n" | "os" | "op" | "opa" => () + | "i" => () | _ => - if typeof(value) === #object && value !== %raw(`null`) { + if typeof(value) === #function { + () + } else if typeof(value) === #object && value !== %raw(`null`) { new->Dict.set( key, cleanUpSchema(value->(magic: unknown => S.t<'a>))->(magic: S.t<'a> => unknown),