diff --git a/CHANGELOG_V7.md b/CHANGELOG_V7.md index be730ae7..2fa958c0 100644 --- a/CHANGELOG_V7.md +++ b/CHANGELOG_V7.md @@ -17,5 +17,4 @@ Plan for V7: - Tree-shakable built-in refinements (postponed) - 2x faster serializeToJsonString - Add S.bigint -- inline literal ints instead of embeding - // TODO: Update doc with changes diff --git a/packages/tests/src/core/Example_test.res b/packages/tests/src/core/Example_test.res index 53754f2e..36c5c96d 100644 --- a/packages/tests/src/core/Example_test.res +++ b/packages/tests/src/core/Example_test.res @@ -69,7 +69,7 @@ test("Compiled parse code snapshot", t => { t->U.assertCompiledCode( ~schema=filmSchema, ~op=#parse, - `i=>{let v0,v1,v2,v3,v8,v9,v14,v15;if(!i||i.constructor!==Object){e[15](i)}v0=i["Id"];if(typeof v0!=="number"||Number.isNaN(v0)){e[0](v0)}v1=i["Title"];if(typeof v1!=="string"){e[1](v1)}v2=i["Tags"];if(v2!==void 0&&(!Array.isArray(v2))){e[2](v2)}if(v2!==void 0){let v5;v5=[];for(let v4=0;v42147483647||v14<-2147483648||v14%1!==0)){e[14](v14)}if(v14!==void 0){v15=v14}else{v15=void 0}return {"id":v0,"title":v1,"tags":v3===void 0?e[4]:v3,"rating":v9,"deprecatedAgeRestriction":v15,}}`, + `i=>{let v0,v1,v2,v3,v8,v9,v14,v15;if(!i||i.constructor!==Object){e[11](i)}v0=i["Id"];if(typeof v0!=="number"||Number.isNaN(v0)){e[0](v0)}v1=i["Title"];if(typeof v1!=="string"){e[1](v1)}v2=i["Tags"];if(v2!==void 0&&(!Array.isArray(v2))){e[2](v2)}if(v2!==void 0){let v5;v5=[];for(let v4=0;v42147483647||v14<-2147483648||v14%1!==0)){e[10](v14)}if(v14!==void 0){v15=v14}else{v15=void 0}return {"id":v0,"title":v1,"tags":v3===void 0?e[4]:v3,"rating":v9,"deprecatedAgeRestriction":v15,}}`, ) }) @@ -77,6 +77,6 @@ test("Compiled serialize code snapshot", t => { t->U.assertCompiledCode( ~schema=filmSchema, ~op=#serialize, - `i=>{let v0,v1,v2,v3,v8,v9;v0=i["tags"];if(v0!==void 0){v1=e[0](v0)}else{v1=void 0}v2=i["rating"];try{v2===e[1]||e[2](v2);v3=v2}catch(v4){if(v4&&v4.s===s){try{v2===e[3]||e[4](v2);v3=v2}catch(v5){if(v5&&v5.s===s){try{v2===e[5]||e[6](v2);v3=v2}catch(v6){if(v6&&v6.s===s){try{v2===e[7]||e[8](v2);v3=v2}catch(v7){if(v7&&v7.s===s){e[9]([v4,v5,v6,v7,])}else{throw v7}}}else{throw v6}}}else{throw v5}}}else{throw v4}}v8=i["deprecatedAgeRestriction"];if(v8!==void 0){v9=e[10](v8)}else{v9=void 0}return {"Id":i["id"],"Title":i["title"],"Tags":v1,"Rating":v3,"Age":v9,}}`, + `i=>{let v0,v1,v2,v3,v8,v9;v0=i["tags"];if(v0!==void 0){v1=e[0](v0)}else{v1=void 0}v2=i["rating"];try{v2==="G"||e[1](v2);v3=v2}catch(v4){if(v4&&v4.s===s){try{v2==="PG"||e[2](v2);v3=v2}catch(v5){if(v5&&v5.s===s){try{v2==="PG13"||e[3](v2);v3=v2}catch(v6){if(v6&&v6.s===s){try{v2==="R"||e[4](v2);v3=v2}catch(v7){if(v7&&v7.s===s){e[5]([v4,v5,v6,v7,])}else{throw v7}}}else{throw v6}}}else{throw v5}}}else{throw v4}}v8=i["deprecatedAgeRestriction"];if(v8!==void 0){v9=e[6](v8)}else{v9=void 0}return {"Id":i["id"],"Title":i["title"],"Tags":v1,"Rating":v3,"Age":v9,}}`, ) }) diff --git a/packages/tests/src/core/S_literal_Array_test.res b/packages/tests/src/core/S_literal_Array_test.res index cb297a90..0155f4e5 100644 --- a/packages/tests/src/core/S_literal_Array_test.res +++ b/packages/tests/src/core/S_literal_Array_test.res @@ -87,7 +87,7 @@ module Common = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{(i===e[0]||Array.isArray(i)&&i.length===2&&i[0]===e[1]&&i[1]===true)||e[2](i);return i}`, + `i=>{(i===e[0]||Array.isArray(i)&&i.length===2&&i[0]==="bar"&&i[1]===true)||e[1](i);return i}`, ) }) @@ -97,7 +97,7 @@ module Common = { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{(i===e[0]||Array.isArray(i)&&i.length===2&&i[0]===e[1]&&i[1]===true)||e[2](i);return i}`, + `i=>{(i===e[0]||Array.isArray(i)&&i.length===2&&i[0]==="bar"&&i[1]===true)||e[1](i);return i}`, ) }) } diff --git a/packages/tests/src/core/S_literal_Number_test.res b/packages/tests/src/core/S_literal_Number_test.res index f9ddbd80..1ff15f40 100644 --- a/packages/tests/src/core/S_literal_Number_test.res +++ b/packages/tests/src/core/S_literal_Number_test.res @@ -62,13 +62,13 @@ module Common = { test("Compiled parse code snapshot", t => { let schema = factory() - t->U.assertCompiledCode(~schema, ~op=#parse, `i=>{i===e[0]||e[1](i);return i}`) + t->U.assertCompiledCode(~schema, ~op=#parse, `i=>{i===123||e[0](i);return i}`) }) test("Compiled serialize code snapshot", t => { let schema = factory() - t->U.assertCompiledCode(~schema, ~op=#serialize, `i=>{i===e[0]||e[1](i);return i}`) + t->U.assertCompiledCode(~schema, ~op=#serialize, `i=>{i===123||e[0](i);return i}`) }) } diff --git a/packages/tests/src/core/S_literal_Object_test.res b/packages/tests/src/core/S_literal_Object_test.res index 9cf7816a..417fb47a 100644 --- a/packages/tests/src/core/S_literal_Object_test.res +++ b/packages/tests/src/core/S_literal_Object_test.res @@ -116,7 +116,7 @@ module Common = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{(i===e[0]||i&&i.constructor===Object&&Object.keys(i).length===1&&i["foo"]===e[1])||e[2](i);return i}`, + `i=>{(i===e[0]||i&&i.constructor===Object&&Object.keys(i).length===1&&i["foo"]==="bar")||e[1](i);return i}`, ) }) @@ -126,7 +126,7 @@ module Common = { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{(i===e[0]||i&&i.constructor===Object&&Object.keys(i).length===1&&i["foo"]===e[1])||e[2](i);return i}`, + `i=>{(i===e[0]||i&&i.constructor===Object&&Object.keys(i).length===1&&i["foo"]==="bar")||e[1](i);return i}`, ) }) } diff --git a/packages/tests/src/core/S_literal_String_test.res b/packages/tests/src/core/S_literal_String_test.res index 13504d53..74088783 100644 --- a/packages/tests/src/core/S_literal_String_test.res +++ b/packages/tests/src/core/S_literal_String_test.res @@ -71,12 +71,16 @@ module Common = { test("Compiled parse code snapshot", t => { let schema = factory() - t->U.assertCompiledCode(~schema, ~op=#parse, `i=>{i===e[0]||e[1](i);return i}`) + t->U.assertCompiledCode(~schema, ~op=#parse, `i=>{i==="ReScript is Great!"||e[0](i);return i}`) }) test("Compiled serialize code snapshot", t => { let schema = factory() - t->U.assertCompiledCode(~schema, ~op=#serialize, `i=>{i===e[0]||e[1](i);return i}`) + t->U.assertCompiledCode( + ~schema, + ~op=#serialize, + `i=>{i==="ReScript is Great!"||e[0](i);return i}`, + ) }) } diff --git a/packages/tests/src/core/S_object_test.res b/packages/tests/src/core/S_object_test.res index a21da339..542bac4e 100644 --- a/packages/tests/src/core/S_object_test.res +++ b/packages/tests/src/core/S_object_test.res @@ -1060,7 +1060,7 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v0,v1,v2,v3;if(!i||i.constructor!==Object){e[6](i)}v3=i["tag"];v3===e[4]||e[5](v3);v1=i["FOO"];if(typeof v1!=="string"){e[1](v1)}v2=i["BAR"];if(typeof v2!=="boolean"){e[2](v2)}for(v0 in i){if(v0!=="tag"&&v0!=="FOO"&&v0!=="BAR"){e[0](v0)}}return {"foo":v1,"bar":v2,"zoo":e[3],}}`, + `i=>{let v0,v1,v2,v3;if(!i||i.constructor!==Object){e[5](i)}v3=i["tag"];v3===0||e[4](v3);v1=i["FOO"];if(typeof v1!=="string"){e[1](v1)}v2=i["BAR"];if(typeof v2!=="boolean"){e[2](v2)}for(v0 in i){if(v0!=="tag"&&v0!=="FOO"&&v0!=="BAR"){e[0](v0)}}return {"foo":v1,"bar":v2,"zoo":e[3],}}`, ) }, ) diff --git a/packages/tests/src/core/S_tuple_test.res b/packages/tests/src/core/S_tuple_test.res index 48c97dea..adba8e0a 100644 --- a/packages/tests/src/core/S_tuple_test.res +++ b/packages/tests/src/core/S_tuple_test.res @@ -277,7 +277,7 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v0,v1,v2;if(!Array.isArray(i)){e[6](i)}if(i.length!==3){e[0](i.length)}v2=i["0"];v2===e[4]||e[5](v2);v0=i["1"];if(typeof v0!=="string"){e[1](v0)}v1=i["2"];if(typeof v1!=="boolean"){e[2](v1)}return {"foo":v0,"bar":v1,"zoo":e[3],}}`, + `i=>{let v0,v1,v2;if(!Array.isArray(i)){e[5](i)}if(i.length!==3){e[0](i.length)}v2=i["0"];v2===0||e[4](v2);v0=i["1"];if(typeof v0!=="string"){e[1](v0)}v1=i["2"];if(typeof v1!=="boolean"){e[2](v1)}return {"foo":v0,"bar":v1,"zoo":e[3],}}`, ) }, ) diff --git a/packages/tests/src/core/S_union_test.res b/packages/tests/src/core/S_union_test.res index 64149b2b..195296eb 100644 --- a/packages/tests/src/core/S_union_test.res +++ b/packages/tests/src/core/S_union_test.res @@ -33,7 +33,7 @@ test("Parses when second struct misses parser", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v0;try{i===e[0]||e[1](i);v0=i}catch(v1){if(v1&&v1.s===s){try{throw e[3];v0=i}catch(v2){if(v2&&v2.s===s){e[4]([v1,v2])}else{throw v2}}}else{throw v1}}return v0}`, + `i=>{let v0;try{i==="apple"||e[0](i);v0=i}catch(v1){if(v1&&v1.s===s){try{throw e[2];v0=i}catch(v2){if(v2&&v2.s===s){e[3]([v1,v2])}else{throw v2}}}else{throw v1}}return v0}`, ) }) @@ -45,7 +45,7 @@ test("Serializes when second struct misses serializer", t => { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0;try{i===e[0]||e[1](i);v0=i}catch(v1){if(v1&&v1.s===s){try{throw e[2];if(typeof i!=="string"){e[3](i)}v0=i}catch(v2){if(v2&&v2.s===s){e[4]([v1,v2,])}else{throw v2}}}else{throw v1}}return v0}`, + `i=>{let v0;try{i==="apple"||e[0](i);v0=i}catch(v1){if(v1&&v1.s===s){try{throw e[1];if(typeof i!=="string"){e[2](i)}v0=i}catch(v2){if(v2&&v2.s===s){e[3]([v1,v2,])}else{throw v2}}}else{throw v1}}return v0}`, ) }) @@ -339,7 +339,7 @@ test("Compiled parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v0;try{i===e[0]||e[1](i);v0=i}catch(v1){if(v1&&v1.s===s){try{i===e[2]||e[3](i);v0=i}catch(v2){if(v2&&v2.s===s){e[4]([v1,v2])}else{throw v2}}}else{throw v1}}return v0}`, + `i=>{let v0;try{i===0||e[0](i);v0=i}catch(v1){if(v1&&v1.s===s){try{i===1||e[1](i);v0=i}catch(v2){if(v2&&v2.s===s){e[2]([v1,v2])}else{throw v2}}}else{throw v1}}return v0}`, ) }) @@ -352,7 +352,7 @@ test("Compiled async parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v0,v1;try{i===e[0]||e[1](i);v0=e[2](i);throw v0}catch(v2){if(v2&&v2.s===s||v2===v0){try{i===e[3]||e[4](i);v1=()=>Promise.resolve(i)}catch(v3){if(v3&&v3.s===s){v1=()=>Promise.any([v2===v0?v2():Promise.reject(v2),Promise.reject(v3)]).catch(t=>{e[5](t.errors)})}else{throw v3}}}else{throw v2}}return v1}`, + `i=>{let v0,v1;try{i===0||e[0](i);v0=e[1](i);throw v0}catch(v2){if(v2&&v2.s===s||v2===v0){try{i===1||e[2](i);v1=()=>Promise.resolve(i)}catch(v3){if(v3&&v3.s===s){v1=()=>Promise.any([v2===v0?v2():Promise.reject(v2),Promise.reject(v3)]).catch(t=>{e[3](t.errors)})}else{throw v3}}}else{throw v2}}return v1}`, ) }) @@ -363,7 +363,7 @@ test("Compiled serialize code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0;try{i===e[0]||e[1](i);v0=i}catch(v1){if(v1&&v1.s===s){try{i===e[2]||e[3](i);v0=i}catch(v2){if(v2&&v2.s===s){e[4]([v1,v2,])}else{throw v2}}}else{throw v1}}return v0}`, + `i=>{let v0;try{i===0||e[0](i);v0=i}catch(v1){if(v1&&v1.s===s){try{i===1||e[1](i);v0=i}catch(v2){if(v2&&v2.s===s){e[2]([v1,v2,])}else{throw v2}}}else{throw v1}}return v0}`, ) }) diff --git a/packages/tests/src/core/S_variant_test.res b/packages/tests/src/core/S_variant_test.res index dd6875e8..e4bb53e7 100644 --- a/packages/tests/src/core/S_variant_test.res +++ b/packages/tests/src/core/S_variant_test.res @@ -135,7 +135,7 @@ test( t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0,v1,v2,v3,v4;v0=i;if(v0!==e[0]){e[1](v0)}v1=e[2];v2=[];v3=v1["0"];v3===true||e[3](v3);v2["0"]=v3;v4=v1["1"];v4===e[4]||e[5](v4);v2["1"]=v4;return v2}`, + `i=>{let v0,v1,v2,v3,v4;v0=i;if(v0!==e[0]){e[1](v0)}v1=e[2];v2=[];v3=v1["0"];v3===true||e[3](v3);v2["0"]=v3;v4=v1["1"];v4===12||e[4](v4);v2["1"]=v4;return v2}`, ) }, ) diff --git a/src/S_Core.bs.mjs b/src/S_Core.bs.mjs index 8b527955..fcfd992b 100644 --- a/src/S_Core.bs.mjs +++ b/src/S_Core.bs.mjs @@ -337,9 +337,50 @@ function build(builder, schema, operation) { return new Function("e", "s", "return " + inlinedFunction)(b.e, symbol); } +function value(literal) { + return literal.value; +} + +function isJsonable(literal) { + return literal.j; +} + +function toString(literal) { + return literal.s; +} + +function arrayCheckBuilder(b, inputVar, literal) { + var items = literal.i; + return "(" + inputVar + "===" + ("e[" + (b.e.push(literal.value) - 1) + "]") + "||Array.isArray(" + inputVar + ")&&" + inputVar + ".length===" + items.length + ( + items.length > 0 ? "&&" + items.map(function (literal, idx) { + return literal.b(b, inputVar + "[" + idx + "]", literal); + }).join("&&") : "" + ) + ")"; +} + +function dictCheckBuilder(b, inputVar, literal) { + var items = literal.i; + var fields = Object.keys(items); + var numberOfFields = fields.length; + return "(" + inputVar + "===" + ("e[" + (b.e.push(value) - 1) + "]") + "||" + inputVar + "&&" + inputVar + ".constructor===Object&&Object.keys(" + inputVar + ").length===" + numberOfFields + ( + numberOfFields > 0 ? "&&" + fields.map(function (field) { + var literal = items[field]; + return literal.b(b, inputVar + "[" + JSON.stringify(field) + "]", literal); + }).join("&&") : "" + ) + ")"; +} + +function inlinedStrictEqualCheckBuilder(param, inputVar, literal) { + return inputVar + "===" + literal.s; +} + +function strictEqualCheckBuilder(b, inputVar, literal) { + return inputVar + "===" + ("e[" + (b.e.push(literal.value) - 1) + "]"); +} + var undefined_value = undefined; -function undefined_b(param, param$1, inputVar) { +function undefined_b(param, inputVar, param$1) { return inputVar + "===void 0"; } @@ -353,21 +394,17 @@ var $$undefined = { var null_value = null; -function null_b(param, param$1, inputVar) { - return inputVar + "===null"; -} - var $$null = { kind: "Null", value: null_value, s: "null", - b: null_b, + b: inlinedStrictEqualCheckBuilder, j: true }; var nan_value = NaN; -function nan_b(param, param$1, inputVar) { +function nan_b(param, inputVar, param$1) { return "Number.isNaN(" + inputVar + ")"; } @@ -379,10 +416,6 @@ var nan = { j: false }; -function strictEqualCheckBuilder(b, value, inputVar) { - return inputVar + "===" + ("e[" + (b.e.push(value) - 1) + "]"); -} - function parseInternal(value) { var typeOfValue = typeof value; if (typeOfValue === "symbol") { @@ -394,14 +427,11 @@ function parseInternal(value) { j: false }; } else if (typeOfValue === "boolean") { - var string = value ? "true" : "false"; return { kind: "Boolean", value: value, - s: string, - b: (function (param, param$1, inputVar) { - return inputVar + "===" + string; - }), + s: value ? "true" : "false", + b: inlinedStrictEqualCheckBuilder, j: true }; } else if (typeOfValue === "string") { @@ -409,7 +439,7 @@ function parseInternal(value) { kind: "String", value: value, s: JSON.stringify(value), - b: strictEqualCheckBuilder, + b: inlinedStrictEqualCheckBuilder, j: true }; } else if (typeOfValue === "function") { @@ -426,7 +456,7 @@ function parseInternal(value) { } else if (Array.isArray(value)) { var items = []; var isJsonable = true; - var string$1 = "["; + var string = "["; for(var idx = 0 ,idx_finish = value.length; idx < idx_finish; ++idx){ var itemValue = value[idx]; var itemLiteral = parseInternal(itemValue); @@ -434,28 +464,22 @@ function parseInternal(value) { isJsonable = false; } if (idx !== 0) { - string$1 = string$1 + ","; + string = string + ","; } - string$1 = string$1 + itemLiteral.s; + string = string + itemLiteral.s; items.push(itemLiteral); } return { kind: "Array", value: value, - s: string$1 + "]", - b: (function (b, value, inputVar) { - return "(" + inputVar + "===" + ("e[" + (b.e.push(value) - 1) + "]") + "||Array.isArray(" + inputVar + ")&&" + inputVar + ".length===" + items.length + ( - items.length > 0 ? "&&" + items.map(function (literal, idx) { - return literal.b(b, literal.value, inputVar + "[" + idx + "]"); - }).join("&&") : "" - ) + ")"; - }), + s: string + "]", + b: arrayCheckBuilder, j: isJsonable, i: Caml_option.some(items) }; } else if (value.constructor === Object) { var items$1 = {}; - var string$2 = "{"; + var string$1 = "{"; var isJsonable$1 = true; var fields = Object.keys(value); var numberOfFields = fields.length; @@ -467,23 +491,16 @@ function parseInternal(value) { isJsonable$1 = false; } if (idx$1 !== 0) { - string$2 = string$2 + ","; + string$1 = string$1 + ","; } - string$2 = string$2 + (JSON.stringify(field) + ":" + itemLiteral$1.s); + string$1 = string$1 + (JSON.stringify(field) + ":" + itemLiteral$1.s); items$1[field] = itemLiteral$1; } return { kind: "Dict", value: value, - s: string$2 + "}", - b: (function (b, value, inputVar) { - return "(" + inputVar + "===" + ("e[" + (b.e.push(value) - 1) + "]") + "||" + inputVar + "&&" + inputVar + ".constructor===Object&&Object.keys(" + inputVar + ").length===" + numberOfFields + ( - numberOfFields > 0 ? "&&" + fields.map(function (field) { - var literal = items$1[field]; - return literal.b(b, literal.value, inputVar + "[" + JSON.stringify(field) + "]"); - }).join("&&") : "" - ) + ")"; - }), + s: string$1 + "}", + b: dictCheckBuilder, j: isJsonable$1, i: Caml_option.some(items$1) }; @@ -506,7 +523,7 @@ function parseInternal(value) { kind: "Number", value: value, s: value.toString(), - b: strictEqualCheckBuilder, + b: inlinedStrictEqualCheckBuilder, j: true }; } @@ -514,8 +531,8 @@ function parseInternal(value) { return { kind: "BigInt", value: value, - s: value.toString(), - b: strictEqualCheckBuilder, + s: value.toString() + "n", + b: inlinedStrictEqualCheckBuilder, j: false }; } @@ -525,18 +542,6 @@ function parse(any) { return parseInternal(any); } -function value(literal) { - return literal.value; -} - -function isJsonable(literal) { - return literal.j; -} - -function toString(literal) { - return literal.s; -} - function loop(_schema) { while(true) { var schema = _schema; @@ -1237,7 +1242,7 @@ function literal(value) { var literal$1 = parseInternal(value); var operationBuilder = function (b, param, path) { var inputVar = toVar(b, b.i); - b.c = b.c + (literal$1.b(b, value, inputVar) + "||" + raiseWithArg(b, path, (function (input) { + b.c = b.c + (literal$1.b(b, inputVar, literal$1) + "||" + raiseWithArg(b, path, (function (input) { return { TAG: "InvalidLiteral", expected: literal$1, diff --git a/src/S_Core.res b/src/S_Core.res index d57158e0..b3bdfdfd 100644 --- a/src/S_Core.res +++ b/src/S_Core.res @@ -124,6 +124,8 @@ module Stdlib = { module BigInt = { @send external toString: bigint => string = "toString" + @inline + let toString = bigint => bigint->toString ++ "n" } module Function = { @@ -702,7 +704,7 @@ module Literal = { @as("s") string: string, @as("b") - checkBuilder: (B.t, ~value: unknown, ~inputVar: string) => string, + checkBuilder: (B.t, ~inputVar: string, ~literal: literal) => string, @as("j") isJsonable: bool, @as("i") @@ -725,12 +727,70 @@ module Literal = { external toInternal: literal => internal = "%identity" external toPublic: internal => literal = "%identity" + @inline + let value = literal => (literal->toInternal).value + + @inline + let isJsonable = literal => (literal->toInternal).isJsonable + + @inline + let toString = literal => (literal->toInternal).string + + let arrayCheckBuilder = (b, ~inputVar, ~literal) => { + let items = (literal->toInternal).items->(Obj.magic: option => array) + + `(${inputVar}===${b->B.embed( + literal->value, + )}||Array.isArray(${inputVar})&&${inputVar}.length===${items + ->Js.Array2.length + ->Stdlib.Int.unsafeToString}` ++ + (items->Js.Array2.length > 0 + ? "&&" ++ + items + ->Js.Array2.mapi((literal, idx) => + b->literal.checkBuilder( + ~inputVar=`${inputVar}[${idx->Stdlib.Int.unsafeToString}]`, + ~literal=literal->toPublic, + ) + ) + ->Js.Array2.joinWith("&&") + : "") ++ ")" + } + + let dictCheckBuilder = (b, ~inputVar, ~literal) => { + let items = (literal->toInternal).items->(Obj.magic: option => dict) + let fields = items->Js.Dict.keys + let numberOfFields = fields->Js.Array2.length + + `(${inputVar}===${b->B.embed( + value, + )}||${inputVar}&&${inputVar}.constructor===Object&&Object.keys(${inputVar}).length===${numberOfFields->Stdlib.Int.unsafeToString}` ++ + (numberOfFields > 0 + ? "&&" ++ + fields + ->Js.Array2.map(field => { + let literal = items->Js.Dict.unsafeGet(field) + b->literal.checkBuilder( + ~inputVar=`${inputVar}[${field->Stdlib.Inlined.Value.fromString}]`, + ~literal=literal->toPublic, + ) + }) + ->Js.Array2.joinWith("&&") + : "") ++ ")" + } + + let inlinedStrictEqualCheckBuilder = (_, ~inputVar, ~literal) => + `${inputVar}===${literal->toString}` + + let strictEqualCheckBuilder = (b, ~inputVar, ~literal) => + `${inputVar}===${b->B.embed(literal->value)}` + let undefined = { kind: Undefined, value: %raw(`undefined`), string: "undefined", isJsonable: false, - checkBuilder: (_, ~value as _, ~inputVar) => `${inputVar}===void 0`, + checkBuilder: (_, ~inputVar, ~literal as _) => `${inputVar}===void 0`, } let null = { @@ -738,7 +798,7 @@ module Literal = { value: %raw(`null`), string: "null", isJsonable: true, - checkBuilder: (_, ~value as _, ~inputVar) => `${inputVar}===null`, + checkBuilder: inlinedStrictEqualCheckBuilder, } let nan = { @@ -746,30 +806,26 @@ module Literal = { value: %raw(`NaN`), string: "NaN", isJsonable: false, - checkBuilder: (_, ~value as _, ~inputVar) => `Number.isNaN(${inputVar})`, + checkBuilder: (_, ~inputVar, ~literal as _) => `Number.isNaN(${inputVar})`, } - let strictEqualCheckBuilder = (b, ~value, ~inputVar) => `${inputVar}===${b->B.embed(value)}` - let string = value => { { kind: String, value: value->castAnyToUnknown, - // FIXME: Test one or two Stdlib.Inlined.Value.fromString string: Stdlib.Inlined.Value.fromString(value), isJsonable: true, - checkBuilder: strictEqualCheckBuilder, + checkBuilder: inlinedStrictEqualCheckBuilder, } } let boolean = value => { - let string = value ? "true" : "false" { kind: Boolean, value: value->castAnyToUnknown, - string, + string: value ? "true" : "false", isJsonable: true, - checkBuilder: (_, ~value as _, ~inputVar) => `${inputVar}===${string}`, + checkBuilder: inlinedStrictEqualCheckBuilder, } } @@ -779,7 +835,7 @@ module Literal = { value: value->castAnyToUnknown, string: value->Js.Float.toString, isJsonable: true, - checkBuilder: strictEqualCheckBuilder, + checkBuilder: inlinedStrictEqualCheckBuilder, } } @@ -799,7 +855,7 @@ module Literal = { value: value->castAnyToUnknown, string: value->BigInt.toString, isJsonable: false, - checkBuilder: strictEqualCheckBuilder, + checkBuilder: inlinedStrictEqualCheckBuilder, } } @@ -870,22 +926,7 @@ module Literal = { items: items->castAnyToUnknown, string: string.contents ++ "}", isJsonable: isJsonable.contents, - checkBuilder: (b, ~value, ~inputVar) => - `(${inputVar}===${b->B.embed( - value, - )}||${inputVar}&&${inputVar}.constructor===Object&&Object.keys(${inputVar}).length===${numberOfFields->Stdlib.Int.unsafeToString}` ++ - (numberOfFields > 0 - ? "&&" ++ - fields - ->Js.Array2.map(field => { - let literal = items->Js.Dict.unsafeGet(field) - b->literal.checkBuilder( - ~value=literal.value, - ~inputVar=`${inputVar}[${field->Stdlib.Inlined.Value.fromString}]`, - ) - }) - ->Js.Array2.joinWith("&&") - : "") ++ ")", + checkBuilder: dictCheckBuilder, } } and array = value => { @@ -911,37 +952,12 @@ module Literal = { items: items->castAnyToUnknown, isJsonable: isJsonable.contents, string: string.contents ++ "]", - checkBuilder: (b, ~value, ~inputVar) => - `(${inputVar}===${b->B.embed( - value, - )}||Array.isArray(${inputVar})&&${inputVar}.length===${items - ->Js.Array2.length - ->Stdlib.Int.unsafeToString}` ++ - (items->Js.Array2.length > 0 - ? "&&" ++ - items - ->Js.Array2.mapi((literal, idx) => - b->literal.checkBuilder( - ~value=literal.value, - ~inputVar=`${inputVar}[${idx->Stdlib.Int.unsafeToString}]`, - ) - ) - ->Js.Array2.joinWith("&&") - : "") ++ ")", + checkBuilder: arrayCheckBuilder, } } @inline let parse = any => any->parseInternal->toPublic - - @inline - let value = literal => (literal->toInternal).value - - @inline - let isJsonable = literal => (literal->toInternal).isJsonable - - @inline - let toString = literal => (literal->toInternal).string } let toInternalLiteral = { @@ -1592,7 +1608,7 @@ let literal = value => { let inputVar = b->B.useInputVar b.code = b.code ++ - `${b->internalLiteral.checkBuilder(~value, ~inputVar)}||${b->B.raiseWithArg( + `${b->internalLiteral.checkBuilder(~inputVar, ~literal)}||${b->B.raiseWithArg( ~path, input => InvalidLiteral({ expected: literal,