From edae1ca80c3f1ce9f9b8e775306eb1c1cfcc988d Mon Sep 17 00:00:00 2001 From: Dmitry Zakharov Date: Thu, 26 Sep 2024 23:09:39 +0400 Subject: [PATCH] v8.3.0 final fixes and improvements --- IDEAS.md | 11 ++- packages/tests/src/benchmark/Benchmark.bs.mjs | 53 ++++++------ packages/tests/src/benchmark/Benchmark.res | 29 +++---- packages/tests/src/benchmark/comparison.js | 4 +- packages/tests/src/core/S_bigint_test.res | 13 +++ src/S_Core.bs.mjs | 80 ++++++++----------- src/S_Core.res | 41 +++++----- 7 files changed, 117 insertions(+), 114 deletions(-) diff --git a/IDEAS.md b/IDEAS.md index 059a397a..94f61812 100644 --- a/IDEAS.md +++ b/IDEAS.md @@ -22,7 +22,8 @@ let trimContract: S.contract string> = S.contract(s => { ## v9 -- Remove ...OrThrow from js/ts api +- Remove ...OrThrow/orRaise +- Add S.compile to Js/ts api - Add mode/flags instead of operation - Expose S.reverse - S.transform(s => { @@ -37,6 +38,7 @@ let trimContract: S.contract string> = S.contract(s => { - Add serializeToJsonString to js api - Fix reverse for object/tuple/variant/recursive - Rename disableNanNumberCheck +- Add tag for BigInt ### Done @@ -46,9 +48,10 @@ let trimContract: S.contract string> = S.contract(s => { - Add S.unwrap - S.isAsyncParse -> S.isAsync - Add: - let convertWith: ('any, t<'value>) => result<'value, error> - let convertToJsonWith: ('any, t<'value>) => result - let convertToJsonStringWith: ('any, t<'value>) => result + let convertAnyWith: ('any, t<'value>) => result<'value, error> + let convertAnyToJsonWith: ('any, t<'value>) => result + let convertAnyToJsonStringWith: ('any, t<'value>) => result + let convertAnyAsyncWith: ('any, t<'value>) => promise> - Deprecated S.parseOrRaiseWith, S.parseAnyOrRaiseWith, S.serializeOrRaiseWith, S.serializeToUnknownOrRaiseWith, S.serializeToJsonStringOrRaiseWith ## v10 diff --git a/packages/tests/src/benchmark/Benchmark.bs.mjs b/packages/tests/src/benchmark/Benchmark.bs.mjs index 428ea5d6..bbd34639 100644 --- a/packages/tests/src/benchmark/Benchmark.bs.mjs +++ b/packages/tests/src/benchmark/Benchmark.bs.mjs @@ -210,56 +210,63 @@ S$RescriptSchema.$$Error.make({ console.timeEnd("S.Error.make"); -run(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(new (Benchmark.default.Suite)(), "Parse string", (function () { +run(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(addWithPrepare(new (Benchmark.default.Suite)(), "Parse string", (function () { + return function () { + return S$RescriptSchema.unwrap(S$RescriptSchema.parseAnyWith("Hello world!", S$RescriptSchema.string)); + }; + })), "Serialize string", (function () { return function () { - return S$RescriptSchema.unwrap(S$RescriptSchema.parseAnyWith("Hello world!", S$RescriptSchema.string)); + return S$RescriptSchema.unwrap(S$RescriptSchema.serializeWith("Hello world!", S$RescriptSchema.string)); }; - })), "Serialize string", (function () { - return function () { - return S$RescriptSchema.unwrap(S$RescriptSchema.serializeWith("Hello world!", S$RescriptSchema.string)); - }; - })).add("Advanced object schema factory", makeAdvancedObjectSchema), "Parse advanced object", (function () { + })).add("Advanced object schema factory", makeAdvancedObjectSchema), "Parse advanced object", (function () { + var schema = makeAdvancedObjectSchema(); + var data = makeTestObject(); + return function () { + return S$RescriptSchema.unwrap(S$RescriptSchema.parseAnyWith(data, schema)); + }; + })), "Assert advanced object - compile", (function () { var schema = makeAdvancedObjectSchema(); var data = makeTestObject(); + var assertFn = S$RescriptSchema.compile(schema, "Any", "Assert", "Sync", true); return function () { - return S$RescriptSchema.unwrap(S$RescriptSchema.parseAnyWith(data, schema)); + assertFn(data); }; - })), "Assert advanced object - compile", (function () { + })), "Assert advanced object", (function () { var schema = makeAdvancedObjectSchema(); var data = makeTestObject(); - var assertFn = S$RescriptSchema.compile(schema, "Any", "Assert", "Sync", true); return function () { - assertFn(data); + S$RescriptSchema.assertOrRaiseWith(data, schema); }; - })), "Assert advanced object", (function () { - var schema = makeAdvancedObjectSchema(); + })), "Create and parse advanced object", (function () { var data = makeTestObject(); return function () { - S$RescriptSchema.assertOrRaiseWith(data, schema); + var schema = makeAdvancedObjectSchema(); + return S$RescriptSchema.unwrap(S$RescriptSchema.parseAnyWith(data, schema)); }; - })), "Create and parse advanced object", (function () { + })), "Parse advanced strict object", (function () { + var schema = makeAdvancedStrictObjectSchema(); var data = makeTestObject(); return function () { - var schema = makeAdvancedObjectSchema(); return S$RescriptSchema.unwrap(S$RescriptSchema.parseAnyWith(data, schema)); }; - })), "Parse advanced strict object", (function () { + })), "Assert advanced strict object", (function () { var schema = makeAdvancedStrictObjectSchema(); var data = makeTestObject(); return function () { - return S$RescriptSchema.unwrap(S$RescriptSchema.parseAnyWith(data, schema)); + S$RescriptSchema.assertOrRaiseWith(data, schema); }; - })), "Assert advanced strict object", (function () { - var schema = makeAdvancedStrictObjectSchema(); + })), "Serialize advanced object", (function () { + var schema = makeAdvancedObjectSchema(); var data = makeTestObject(); return function () { - S$RescriptSchema.assertOrRaiseWith(data, schema); + return S$RescriptSchema.unwrap(S$RescriptSchema.serializeWith(data, schema)); }; - })), "Serialize advanced object", (function () { + })), "Serialize advanced object - compile", (function () { var schema = makeAdvancedObjectSchema(); var data = makeTestObject(); + var fn = S$RescriptSchema.compile(S$RescriptSchema.$tildeexperimentalReverse(schema), "Any", "Output", "Sync", false); return function () { - return S$RescriptSchema.unwrap(S$RescriptSchema.serializeWith(data, schema)); + return fn(data); }; }))); diff --git a/packages/tests/src/benchmark/Benchmark.res b/packages/tests/src/benchmark/Benchmark.res index 13b56fc3..d0add5c5 100644 --- a/packages/tests/src/benchmark/Benchmark.res +++ b/packages/tests/src/benchmark/Benchmark.res @@ -190,27 +190,9 @@ module CrazyUnion = { Console.time("testData2 parse") let _ = S.parseWith(json, schema)->S.unwrap Console.timeEnd("testData2 parse") - - // Console.log((schema->Obj.magic)["parseOrThrow"]["toString"]()) } } -// Full -// testData1 serialize: 5.414s -// testData1 parse: 5.519s -// testData2 serialize: 70.864ms -// testData2 parse: 70.967ms - -// Wip -// testData1 serialize: 5.843s -// testData1 parse: 5.625ms -// testData2 serialize: 64.489ms -// testData2 parse: 0.836ms - -// Partial -// testData1 serialize: 1.802ms -// testData1 parse: 1.411ms -// 734 Error.make CrazyUnion.test() let data = makeTestObject() @@ -312,6 +294,17 @@ Suite.make() data->S.serializeWith(schema)->S.unwrap } }) +->Suite.addWithPrepare("Serialize advanced object - compile", () => { + let schema = makeAdvancedObjectSchema() + let data = makeTestObject() + let fn = + schema + ->S.\"~experimentalReverse" + ->S.compile(~input=Any, ~output=Output, ~mode=Sync, ~typeValidation=false) + () => { + fn(data) + } +}) ->Suite.run /* diff --git a/packages/tests/src/benchmark/comparison.js b/packages/tests/src/benchmark/comparison.js index 79c3f38c..e59664fb 100644 --- a/packages/tests/src/benchmark/comparison.js +++ b/packages/tests/src/benchmark/comparison.js @@ -149,7 +149,7 @@ new B.Suite() }); }) .add("rescript-schema (parse)", () => { - return schema.parseOrThrow(data); + return schema.parse(data); }) .add("rescript-schema (create + parse)", () => { const schema = S.object({ @@ -165,7 +165,7 @@ new B.Suite() bool: S.boolean, }), }); - return schema.parseOrThrow(data); + return schema.parse(data); }) .on("cycle", (event) => { console.log(String(event.target)); diff --git a/packages/tests/src/core/S_bigint_test.res b/packages/tests/src/core/S_bigint_test.res index b03fcc0f..3bc63803 100644 --- a/packages/tests/src/core/S_bigint_test.res +++ b/packages/tests/src/core/S_bigint_test.res @@ -25,6 +25,19 @@ module Common = { ) }) + test("Fails to convert to Json", t => { + let schema = factory() + + t->U.assertErrorResult( + value->S.convertAnyToJsonWith(schema), + { + code: InvalidJsonSchema(schema->S.toUnknown), + operation: SerializeToJson, + path: S.Path.empty, + }, + ) + }) + test("BigInt name", t => { let schema = factory() t->Assert.is(schema->S.name, "BigInt", ()) diff --git a/src/S_Core.bs.mjs b/src/S_Core.bs.mjs index fc399253..6bdde853 100644 --- a/src/S_Core.bs.mjs +++ b/src/S_Core.bs.mjs @@ -384,6 +384,11 @@ function noop(_b, input, param, param$1) { return input; } +function invalidJson(b, input, selfSchema, path) { + registerInvalidJson(b, selfSchema, path); + return input; +} + function noopOperation(i) { return i; } @@ -727,6 +732,16 @@ function $tildeexperimentalReverse(schema) { return schema.r(); } +function wrapExnToError(exn) { + if ((exn&&exn.s===symbol)) { + return { + TAG: "Error", + _0: exn + }; + } + throw exn; +} + function asyncPrepareOk(value) { return { TAG: "Ok", @@ -734,13 +749,6 @@ function asyncPrepareOk(value) { }; } -function asyncPrepareError(jsExn) { - return { - TAG: "Error", - _0: getOrRethrow(jsExn) - }; -} - function convertAnyWith(any, schema) { try { return { @@ -748,12 +756,8 @@ function convertAnyWith(any, schema) { _0: (schema[0] ? undefined : (schema[0] = compile(schema.b, schema, 0), undefined), schema[0](any)) }; } - catch (raw_exn){ - var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); - return { - TAG: "Error", - _0: getOrRethrow(exn) - }; + catch (exn){ + return wrapExnToError(exn); } } @@ -764,12 +768,8 @@ function convertAnyToJsonWith(any, schema) { _0: (schema[8] ? undefined : (schema[8] = compile(schema.b, schema, 8), undefined), schema[8](any)) }; } - catch (raw_exn){ - var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); - return { - TAG: "Error", - _0: getOrRethrow(exn) - }; + catch (exn){ + return wrapExnToError(exn); } } @@ -780,25 +780,17 @@ function convertAnyToJsonStringWith(any, schema) { _0: (schema[16] ? undefined : (schema[16] = compile(schema.b, schema, 16), undefined), schema[16](any)) }; } - catch (raw_exn){ - var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); - return { - TAG: "Error", - _0: getOrRethrow(exn) - }; + catch (exn){ + return wrapExnToError(exn); } } function convertAnyAsyncWith(any, schema) { try { - return (schema[2] ? undefined : (schema[2] = compile(schema.b, schema, 2), undefined), schema[2](any)).then(asyncPrepareOk, asyncPrepareError); + return (schema[2] ? undefined : (schema[2] = compile(schema.b, schema, 2), undefined), schema[2](any)).then(asyncPrepareOk, wrapExnToError); } - catch (raw_exn){ - var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); - return Promise.resolve({ - TAG: "Error", - _0: getOrRethrow(exn) - }); + catch (exn){ + return Promise.resolve(wrapExnToError(exn)); } } @@ -828,14 +820,10 @@ function parseAnyWith(any, schema) { function parseAnyAsyncWith(any, schema) { try { - return (schema[3] ? undefined : (schema[3] = compile(schema.b, schema, 3), undefined), schema[3](any)).then(asyncPrepareOk, asyncPrepareError); + return (schema[3] ? undefined : (schema[3] = compile(schema.b, schema, 3), undefined), schema[3](any)).then(asyncPrepareOk, wrapExnToError); } - catch (raw_exn){ - var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); - return Promise.resolve({ - TAG: "Error", - _0: getOrRethrow(exn) - }); + catch (exn){ + return Promise.resolve(wrapExnToError(exn)); } } @@ -1251,6 +1239,7 @@ function custom(name, definer) { return makeSchema((function () { return name; }), "Unknown", empty, (function (b, input, selfSchema, path) { + registerInvalidJson(b, selfSchema, path); var match = definer(effectCtx(b, selfSchema, path)); var parser = match.p; if (parser !== undefined) { @@ -1272,6 +1261,7 @@ function custom(name, definer) { return makeReverseSchema((function () { return name; }), "Unknown", empty, (function (b, input, selfSchema, path) { + registerInvalidJson(b, selfSchema, path); var match = definer(effectCtx(b, selfSchema, path)); var serializer = match.s; if (serializer !== undefined) { @@ -1292,10 +1282,7 @@ function literal(value) { }), { TAG: "Literal", _0: literal$1 - }, empty, literal$1.j ? noop : (function (b, input, selfSchema, path) { - registerInvalidJson(b, selfSchema, path); - return input; - }), (function (b, inputVar) { + }, empty, literal$1.j ? noop : invalidJson, (function (b, inputVar) { return literal$1.f(b, inputVar, literal$1); }), toSelf); } @@ -1931,10 +1918,7 @@ function factory$4(schema) { }), typeFilter$1, onlyChild(factory$4, schema)); } -var schema$1 = makeSchema(primitiveName, "Unknown", empty, (function (b, input, selfSchema, path) { - registerInvalidJson(b, selfSchema, path); - return input; - }), undefined, toSelf); +var schema$1 = makeSchema(primitiveName, "Unknown", empty, invalidJson, undefined, toSelf); var metadataId$1 = "rescript-schema:String.refinements"; @@ -2041,7 +2025,7 @@ function typeFilter$6(_b, inputVar) { return "typeof " + inputVar + "!==\"bigint\""; } -var schema$6 = makePrimitiveSchema("Unknown", noop, typeFilter$6); +var schema$6 = makePrimitiveSchema("Unknown", invalidJson, typeFilter$6); schema$6.n = (() => "BigInt"); diff --git a/src/S_Core.res b/src/S_Core.res index c1d946be..47c66b17 100644 --- a/src/S_Core.res +++ b/src/S_Core.res @@ -25,7 +25,7 @@ module Stdlib = { type t<+'a> = promise<'a> @send - external thenResolveWithCatch: (t<'a>, 'a => 'b, Js.Exn.t => 'b) => t<'b> = "then" + external thenResolveWithCatch: (t<'a>, 'a => 'b, exn => 'b) => t<'b> = "then" @send external thenResolve: (t<'a>, 'a => 'b) => t<'b> = "then" @@ -770,6 +770,11 @@ module Builder = { let noop = make((_b, ~input, ~selfSchema as _, ~path as _) => input) + let invalidJson = make((b, ~input, ~selfSchema, ~path) => { + b->B.registerInvalidJson(~selfSchema, ~path) + input + }) + let noopOperation = i => i->Obj.magic @inline @@ -1278,6 +1283,14 @@ let callMemoizedOperation = (schema, operation, input) => { (schema->Obj.magic->Js.Dict.unsafeGet(operation->Stdlib.Int.unsafeToString))(input) } +let wrapExnToError = exn => { + if %raw("exn&&exn.s===symbol") { + Error(exn->(Obj.magic: exn => error)) + } else { + raise(exn) + } +} + @inline let useSyncOperation = (schema, operation, input) => { try { @@ -1285,24 +1298,20 @@ let useSyncOperation = (schema, operation, input) => { ->callMemoizedOperation(operation, input) ->Ok } catch { - | exn => exn->InternalError.getOrRethrow->Error + | _ => wrapExnToError(%raw(`exn`)) } } let asyncPrepareOk = value => Ok(value->castUnknownToAny) -let asyncPrepareError = jsExn => { - jsExn->(Obj.magic: Js.Exn.t => exn)->InternalError.getOrRethrow->Error -} - @inline let useAsyncOperation = (schema, operation, input) => { try { schema ->callMemoizedOperation(operation->Operation.addFlag(Operation.Flag.async), input) - ->Stdlib.Promise.thenResolveWithCatch(asyncPrepareOk, asyncPrepareError) + ->Stdlib.Promise.thenResolveWithCatch(asyncPrepareOk, wrapExnToError) } catch { - | exn => exn->InternalError.getOrRethrow->Error->Stdlib.Promise.resolve + | _ => wrapExnToError(%raw(`exn`))->Stdlib.Promise.resolve } } @@ -1861,6 +1870,7 @@ let custom = (name, definer) => { ~metadataMap=Metadata.Map.empty, ~tagged=Unknown, ~builder=Builder.make((b, ~input, ~selfSchema, ~path) => { + b->B.registerInvalidJson(~selfSchema, ~path) switch definer(b->B.effectCtx(~selfSchema, ~path)) { | {parser, asyncParser: ?None} => b->B.embedSyncOperation(~input, ~fn=parser) | {parser: ?None, asyncParser} => b->B.embedAsyncOperation(~input, ~fn=asyncParser) @@ -1880,6 +1890,7 @@ let custom = (name, definer) => { ~name=() => name, ~tagged=Unknown, ~builder=Builder.make((b, ~input, ~selfSchema, ~path) => { + b->B.registerInvalidJson(~selfSchema, ~path) switch definer(b->B.effectCtx(~selfSchema, ~path)) { | {serializer} => b->B.embedSyncOperation(~input, ~fn=serializer) | {parser: ?None, asyncParser: ?None, serializer: ?None} => input @@ -1904,12 +1915,7 @@ let literal = value => { ~name=() => literal->Literal.toString, ~metadataMap=Metadata.Map.empty, ~tagged=Literal(literal), - ~builder=literal->Literal.isJsonable - ? Builder.noop - : Builder.make((b, ~input, ~selfSchema, ~path) => { - b->B.registerInvalidJson(~selfSchema, ~path) - input - }), + ~builder=literal->Literal.isJsonable ? Builder.noop : Builder.invalidJson, ~maybeTypeFilter=Some((b, ~inputVar) => b->internalLiteral.filterBuilder(~inputVar, ~literal)), ~reverse=Reverse.toSelf, ) @@ -2852,10 +2858,7 @@ module Unknown = { let schema = makeSchema( ~name=primitiveName, ~tagged=Unknown, - ~builder=Builder.make((b, ~input, ~selfSchema, ~path) => { - b->B.registerInvalidJson(~selfSchema, ~path) - input - }), + ~builder=Builder.invalidJson, ~metadataMap=Metadata.Map.empty, ~maybeTypeFilter=None, ~reverse=Reverse.toSelf, @@ -3049,7 +3052,7 @@ module BigInt = { let schema = makePrimitiveSchema( ~tagged=Unknown, // TODO: Add BigInt in v9 - ~builder=Builder.noop, + ~builder=Builder.invalidJson, ~maybeTypeFilter=Some(typeFilter), ) (schema->Obj.magic)["n"] = %raw(`() => "BigInt"`)