diff --git a/CHANGELOG_V7.md b/CHANGELOG_V7.md index 36075b07..c0425e32 100644 --- a/CHANGELOG_V7.md +++ b/CHANGELOG_V7.md @@ -17,3 +17,4 @@ - Improved tree-shaking - Can get the info from the `tagged` type: `JSON` -> `JSON({validated: bool})` - Removed `s.failWithError`. Use `S.Error.raise` instead +- Removed async support for `S.union`. Please create an issue if you used the feature. diff --git a/packages/tests/src/core/Example_test.res b/packages/tests/src/core/Example_test.res index 2521e6e4..ee9905ce 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=>{if(!i||i.constructor!==Object){e[11](i)}let v0,v1,v2,v3,v8,v9,v14,v15;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,}}`, + `i=>{if(!i||i.constructor!==Object){e[11](i)}let v0=i["Id"],v1=i["Title"],v2=i["Tags"],v9=i["Rating"],v11=i["Age"];if(typeof v0!=="number"||Number.isNaN(v0)){e[0](v0)}if(typeof v1!=="string"){e[1](v1)}if(v2!==void 0&&(!Array.isArray(v2))){e[2](v2)}let v8;if(v2!==void 0){let v7=[];for(let v3=0;v32147483647||v11<-2147483648||v11%1!==0)){e[10](v11)}return {"id":v0,"title":v1,"tags":v8===void 0?e[4]:v8,"rating":v10,"deprecatedAgeRestriction":v11,}}`, ) }) @@ -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==="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,}}`, + `i=>{let v0=i["tags"],v1,v2=i["rating"],v3,v4=i["deprecatedAgeRestriction"],v5;if(v0!==void 0){v1=e[0](v0)}try{v2==="G"||e[1](v2);v3=v2}catch(e0){try{v2==="PG"||e[2](v2);v3=v2}catch(e1){try{v2==="PG13"||e[3](v2);v3=v2}catch(e2){try{v2==="R"||e[4](v2);v3=v2}catch(e3){e[5]([e0,e1,e2,e3,])}}}}if(v4!==void 0){v5=e[6](v4)}return {"Id":i["id"],"Title":i["title"],"Tags":v1,"Rating":v3,"Age":v5,}}`, ) }) diff --git a/packages/tests/src/core/S_Option_getOrWith_test.res b/packages/tests/src/core/S_Option_getOrWith_test.res index fc9ae612..4b022cfc 100644 --- a/packages/tests/src/core/S_Option_getOrWith_test.res +++ b/packages/tests/src/core/S_Option_getOrWith_test.res @@ -62,7 +62,7 @@ test("Compiled parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(i!==void 0&&(typeof i!=="boolean")){e[1](i)}let v0;if(i!==void 0){v0=i}else{v0=void 0}return v0===void 0?e[0]():v0}`, + `i=>{if(i!==void 0&&(typeof i!=="boolean")){e[1](i)}return i===void 0?e[0]():i}`, ) }) @@ -76,7 +76,7 @@ test("Compiled async parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(i!==void 0&&(typeof i!=="boolean")){e[2](i)}let v0,v3;if(i!==void 0){let v1;v1=e[0](i);v0=v1}else{v0=()=>Promise.resolve(void 0)}v3=()=>v0().then(v2=>{return v2===void 0?e[1]():v2});return v3}`, + `i=>{if(i!==void 0&&(typeof i!=="boolean")){e[2](i)}let v0;if(i!==void 0){v0=e[0](i)}else{v0=()=>Promise.resolve(void 0)}return ()=>v0().then(v1=>{return v1===void 0?e[1]():v1})}`, ) }) @@ -86,6 +86,6 @@ test("Compiled serialize code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0;if(i!==void 0){v0=e[0](i)}else{v0=void 0}return v0}`, + `i=>{let v0;if(i!==void 0){v0=e[0](i)}return v0}`, ) }) diff --git a/packages/tests/src/core/S_Option_getOr_test.res b/packages/tests/src/core/S_Option_getOr_test.res index a32ae349..081e42d8 100644 --- a/packages/tests/src/core/S_Option_getOr_test.res +++ b/packages/tests/src/core/S_Option_getOr_test.res @@ -86,7 +86,7 @@ test("Compiled parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(i!==void 0&&(typeof i!=="boolean")){e[1](i)}let v0;if(i!==void 0){v0=i}else{v0=void 0}return v0===void 0?e[0]:v0}`, + `i=>{if(i!==void 0&&(typeof i!=="boolean")){e[1](i)}return i===void 0?e[0]:i}`, ) }) @@ -100,7 +100,7 @@ test("Compiled async parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(i!==void 0&&(typeof i!=="boolean")){e[2](i)}let v0,v3;if(i!==void 0){let v1;v1=e[0](i);v0=v1}else{v0=()=>Promise.resolve(void 0)}v3=()=>v0().then(v2=>{return v2===void 0?e[1]:v2});return v3}`, + `i=>{if(i!==void 0&&(typeof i!=="boolean")){e[2](i)}let v0;if(i!==void 0){v0=e[0](i)}else{v0=()=>Promise.resolve(void 0)}return ()=>v0().then(v1=>{return v1===void 0?e[1]:v1})}`, ) }) @@ -110,6 +110,6 @@ test("Compiled serialize code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0;if(i!==void 0){v0=e[0](i)}else{v0=void 0}return v0}`, + `i=>{let v0;if(i!==void 0){v0=e[0](i)}return v0}`, ) }) diff --git a/packages/tests/src/core/S_array_test.res b/packages/tests/src/core/S_array_test.res index 56b28a2d..48636770 100644 --- a/packages/tests/src/core/S_array_test.res +++ b/packages/tests/src/core/S_array_test.res @@ -52,7 +52,7 @@ module CommonWithNested = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!Array.isArray(i)){e[1](i)}let v1;v1=[];for(let v0=0;v0{if(!Array.isArray(i)){e[1](i)}let v4=[];for(let v0=0;v0U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!Array.isArray(i)){e[1](i)}let v1,v5;v1=[];for(let v0=0;v0{try{return v3().catch(v2=>{if(v2&&v2.s===s){v2.path=""+\'["\'+v0+\'"]\'+v2.path}throw v2})}catch(v2){if(v2&&v2.s===s){v2.path=""+\'["\'+v0+\'"]\'+v2.path}throw v2}};}catch(v2){if(v2&&v2.s===s){v2.path=""+\'["\'+v0+\'"]\'+v2.path}throw v2}v1.push(v4)}v5=()=>Promise.all(v1.map(t=>t()));return v5}`, + `i=>{if(!Array.isArray(i)){e[1](i)}let v4=[];for(let v0=0;v0{try{return v2().catch(v1=>{if(v1&&v1.s===s){v1.path=""+\'["\'+v0+\'"]\'+v1.path}throw v1})}catch(v1){if(v1&&v1.s===s){v1.path=""+\'["\'+v0+\'"]\'+v1.path}throw v1}}}catch(v1){if(v1&&v1.s===s){v1.path=""+\'["\'+v0+\'"]\'+v1.path}throw v1}v4.push(v3)}return ()=>Promise.all(v4.map(t=>t()))}`, ) }) @@ -79,7 +79,7 @@ module CommonWithNested = { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v1;v1=[];for(let v0=0;v0{let v5=[];for(let v0=0;v0 { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{try{if(typeof i!=="boolean"){e[1](i)}}catch(v0){if(v0&&v0.s===s){i=e[0](i,v0)}else{throw v0}}return i}`, + // TODO: Improve to return i without reassigning + `i=>{let v1;try{if(typeof i!=="boolean"){e[1](i)}v1=i}catch(v0){if(v0&&v0.s===s){v1=e[0](i,v0)}else{throw v0}}return v1}`, ) }) @@ -127,7 +128,7 @@ test("Compiled async parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v1,v2;try{if(typeof i!=="boolean"){e[1](i)}v1=e[2](i);v2=()=>{try{return v1().catch(v0=>{if(v0&&v0.s===s){return e[0](i,v0)}else{throw v0}})}catch(v0){if(v0&&v0.s===s){return Promise.resolve(e[0](i,v0))}else{throw v0}}};}catch(v0){if(v0&&v0.s===s){v2=()=>Promise.resolve(e[0](i,v0))}else{throw v0}}return v2}`, + `i=>{let v1,v2;try{if(typeof i!=="boolean"){e[1](i)}v1=e[2](i);v2=()=>{try{return v1().catch(v0=>{if(v0&&v0.s===s){return e[0](i,v0)}else{throw v0}})}catch(v0){if(v0&&v0.s===s){return Promise.resolve(e[0](i,v0))}else{throw v0}}}}catch(v0){if(v0&&v0.s===s){v2=()=>Promise.resolve(e[0](i,v0))}else{throw v0}}return v2}`, ) }) diff --git a/packages/tests/src/core/S_dict_test.res b/packages/tests/src/core/S_dict_test.res index 3fb67ac4..37ca611a 100644 --- a/packages/tests/src/core/S_dict_test.res +++ b/packages/tests/src/core/S_dict_test.res @@ -52,7 +52,7 @@ module CommonWithNested = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!i||i.constructor!==Object){e[1](i)}let v1;v1={};for(let v0 in i){let v3;try{v3=i[v0];if(typeof v3!=="string"){e[0](v3)}}catch(v2){if(v2&&v2.s===s){v2.path=""+\'["\'+v0+\'"]\'+v2.path}throw v2}v1[v0]=v3}return v1}`, + `i=>{if(!i||i.constructor!==Object){e[1](i)}let v4={};for(let v0 in i){let v2=i[v0],v3;try{if(typeof v2!=="string"){e[0](v2)}v3=v2}catch(v1){if(v1&&v1.s===s){v1.path=""+\'["\'+v0+\'"]\'+v1.path}throw v1}v4[v0]=v3}return v4}`, ) }) @@ -62,7 +62,7 @@ module CommonWithNested = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!i||i.constructor!==Object){e[1](i)}let v1,v9;v1={};for(let v0 in i){let v3,v4;try{v3=e[0](i[v0]);v4=()=>{try{return v3().catch(v2=>{if(v2&&v2.s===s){v2.path=""+'["'+v0+'"]'+v2.path}throw v2})}catch(v2){if(v2&&v2.s===s){v2.path=""+'["'+v0+'"]'+v2.path}throw v2}};}catch(v2){if(v2&&v2.s===s){v2.path=""+'["'+v0+'"]'+v2.path}throw v2}v1[v0]=v4}v9=()=>new Promise((v5,v6)=>{let v8=Object.keys(v1).length;for(let v0 in v1){v1[v0]().then(v7=>{v1[v0]=v7;if(v8--===1){v5(v1)}},v6)}});return v9}`, + `i=>{if(!i||i.constructor!==Object){e[1](i)}let v4={};for(let v0 in i){let v2,v3;try{v2=e[0](i[v0]);v3=()=>{try{return v2().catch(v1=>{if(v1&&v1.s===s){v1.path=""+\'["\'+v0+\'"]\'+v1.path}throw v1})}catch(v1){if(v1&&v1.s===s){v1.path=""+\'["\'+v0+\'"]\'+v1.path}throw v1}}}catch(v1){if(v1&&v1.s===s){v1.path=""+\'["\'+v0+\'"]\'+v1.path}throw v1}v4[v0]=v3}return ()=>new Promise((v5,v6)=>{let v8=Object.keys(v4).length;for(let v0 in v4){v4[v0]().then(v7=>{v4[v0]=v7;if(v8--===1){v5(v4)}},v6)}})}`, ) }) @@ -78,7 +78,7 @@ module CommonWithNested = { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v1;v1={};for(let v0 in i){let v3,v4;try{v3=i[v0];if(v3!==void 0){v4=e[0](v3)}else{v4=void 0}}catch(v2){if(v2&&v2.s===s){v2.path=""+'["'+v0+'"]'+v2.path}throw v2}v1[v0]=v4}return v1}`, + `i=>{let v5={};for(let v0 in i){let v4;try{let v2=i[v0],v3;if(v2!==void 0){v3=e[0](v2)}v4=v3}catch(v1){if(v1&&v1.s===s){v1.path=""+\'["\'+v0+\'"]\'+v1.path}throw v1}v5[v0]=v4}return v5}`, ) }) } diff --git a/packages/tests/src/core/S_jsonString_test.res b/packages/tests/src/core/S_jsonString_test.res index f36979d4..51dbfcab 100644 --- a/packages/tests/src/core/S_jsonString_test.res +++ b/packages/tests/src/core/S_jsonString_test.res @@ -81,7 +81,7 @@ test("Compiled async parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(typeof i!=="string"){e[3](i)}let v0,v1;try{v0=JSON.parse(i)}catch(t){e[0](t.message)}if(typeof v0!=="boolean"){e[1](v0)}v1=e[2](v0);return v1}`, + `i=>{if(typeof i!=="string"){e[3](i)}let v0;try{v0=JSON.parse(i)}catch(t){e[0](t.message)}if(typeof v0!=="boolean"){e[1](v0)}return e[2](v0)}`, ) }) diff --git a/packages/tests/src/core/S_literal_Undefined_test.res b/packages/tests/src/core/S_literal_Undefined_test.res index 43ab9c03..555d8fde 100644 --- a/packages/tests/src/core/S_literal_Undefined_test.res +++ b/packages/tests/src/core/S_literal_Undefined_test.res @@ -48,12 +48,12 @@ module Common = { test("Compiled parse code snapshot", t => { let schema = factory() - t->U.assertCompiledCode(~schema, ~op=#parse, `i=>{i===void 0||e[0](i);return i}`) + t->U.assertCompiledCode(~schema, ~op=#parse, `i=>{i===undefined||e[0](i);return i}`) }) test("Compiled serialize code snapshot", t => { let schema = factory() - t->U.assertCompiledCode(~schema, ~op=#serialize, `i=>{i===void 0||e[0](i);return i}`) + t->U.assertCompiledCode(~schema, ~op=#serialize, `i=>{i===undefined||e[0](i);return i}`) }) } diff --git a/packages/tests/src/core/S_null_test.res b/packages/tests/src/core/S_null_test.res index 2e945a74..72d2fc08 100644 --- a/packages/tests/src/core/S_null_test.res +++ b/packages/tests/src/core/S_null_test.res @@ -48,7 +48,7 @@ module Common = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v0;if(i!==null){let v1;v1=e[0](i);v0=v1}else{v0=()=>Promise.resolve(void 0)}return v0}`, + `i=>{let v0;if(i!==null){v0=e[0](i)}else{v0=()=>Promise.resolve(void 0)}return v0}`, ) }) diff --git a/packages/tests/src/core/S_nullable_test.res b/packages/tests/src/core/S_nullable_test.res index 83e2d33f..b934f586 100644 --- a/packages/tests/src/core/S_nullable_test.res +++ b/packages/tests/src/core/S_nullable_test.res @@ -38,7 +38,7 @@ module NullCommon = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(i!==void 0&&(i!==null&&(typeof i!=="string"))){e[0](i)}let v0;if(i!==void 0){let v1;if(i!==null){v1=i}else{v1=void 0}v0=v1}else{v0=void 0}return v0}`, + `i=>{if(i!==void 0&&(i!==null&&(typeof i!=="string"))){e[0](i)}let v1;if(i!==void 0){let v0;if(i!==null){v0=i}else{v0=void 0}v1=v0}return v1}`, ) }) @@ -50,7 +50,7 @@ module NullCommon = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v0;if(i!==void 0){let v1;if(i!==null){let v2;v2=e[0](i);v1=v2}else{v1=()=>Promise.resolve(void 0)}v0=v1}else{v0=()=>Promise.resolve(void 0)}return v0}`, + `i=>{let v1;if(i!==void 0){let v0;if(i!==null){v0=e[0](i)}else{v0=()=>Promise.resolve(void 0)}v1=v0}else{v1=()=>Promise.resolve(void 0)}return v1}`, ) }) @@ -60,7 +60,7 @@ module NullCommon = { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0;if(i!==void 0){let v1,v2;v1=e[0](i);if(v1!==void 0){v2=e[1](v1)}else{v2=null}v0=v2}else{v0=void 0}return v0}`, + `i=>{let v2;if(i!==void 0){let v0=e[0](i),v1;if(v0!==void 0){v1=e[1](v0)}else{v1=null}v2=v1}return v2}`, ) }) } diff --git a/packages/tests/src/core/S_object_test.res b/packages/tests/src/core/S_object_test.res index cb4b5134..8820c701 100644 --- a/packages/tests/src/core/S_object_test.res +++ b/packages/tests/src/core/S_object_test.res @@ -986,7 +986,7 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!i||i.constructor!==Object){e[2](i)}let v0,v1;v0=i["foo"];if(typeof v0!=="string"){e[0](v0)}v1=i["bar"];if(typeof v1!=="boolean"){e[1](v1)}return {"foo":v0,"bar":v1,}}`, + `i=>{if(!i||i.constructor!==Object){e[2](i)}let v0=i["foo"],v1=i["bar"];if(typeof v0!=="string"){e[0](v0)}if(typeof v1!=="boolean"){e[1](v1)}return {"foo":v0,"bar":v1,}}`, ) }) @@ -1004,7 +1004,7 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!i||i.constructor!==Object){e[2](i)}let v0,v1,v2;v0=e[0](i["foo"]);v1=i["bar"];if(typeof v1!=="boolean"){e[1](v1)}v2=()=>Promise.all([v0()]).then(([v0])=>({"foo":v0,"bar":v1,}));return v2}`, + `i=>{if(!i||i.constructor!==Object){e[2](i)}let v0,v1=i["bar"];v0=e[0](i["foo"]);if(typeof v1!=="boolean"){e[1](v1)}return ()=>Promise.all([v0()]).then(([v0])=>({"foo":v0,"bar":v1,}))}`, ) }) @@ -1030,7 +1030,7 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!i||i.constructor!==Object){e[3](i)}let v0,v1,v2;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!=="foo"&&v0!=="bar"){e[0](v0)}}return {"foo":v1,"bar":v2,}}`, + `i=>{if(!i||i.constructor!==Object){e[3](i)}let v0,v1=i["foo"],v2=i["bar"];if(typeof v1!=="string"){e[1](v1)}if(typeof v2!=="boolean"){e[2](v2)}for(v0 in i){if(v0!=="foo"&&v0!=="bar"){e[0](v0)}}return {"foo":v1,"bar":v2,}}`, ) }) @@ -1060,7 +1060,7 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!i||i.constructor!==Object){e[5](i)}let v0,v1,v2,v3;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],}}`, + `i=>{if(!i||i.constructor!==Object){e[5](i)}let v0,v1=i["FOO"],v2=i["BAR"],v3=i["tag"];v3===0||e[4](v3);if(typeof v1!=="string"){e[1](v1)}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_option_test.res b/packages/tests/src/core/S_option_test.res index 97930cf4..97894049 100644 --- a/packages/tests/src/core/S_option_test.res +++ b/packages/tests/src/core/S_option_test.res @@ -38,7 +38,7 @@ module Common = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(i!==void 0&&(typeof i!=="string")){e[0](i)}let v0;if(i!==void 0){v0=i}else{v0=void 0}return v0}`, + `i=>{if(i!==void 0&&(typeof i!=="string")){e[0](i)}return i}`, ) }) @@ -48,7 +48,7 @@ module Common = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v0;if(i!==void 0){let v1;v1=e[0](i);v0=v1}else{v0=()=>Promise.resolve(void 0)}return v0}`, + `i=>{let v0;if(i!==void 0){v0=e[0](i)}else{v0=()=>Promise.resolve(void 0)}return v0}`, ) }) @@ -58,7 +58,7 @@ module Common = { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0;if(i!==void 0){v0=e[0](i)}else{v0=void 0}return v0}`, + `i=>{let v0;if(i!==void 0){v0=e[0](i)}return v0}`, ) }) } diff --git a/packages/tests/src/core/S_parseAnyAsyncInStepsWith_test.res b/packages/tests/src/core/S_parseAnyAsyncInStepsWith_test.res index 715d308b..ce41f86b 100644 --- a/packages/tests/src/core/S_parseAnyAsyncInStepsWith_test.res +++ b/packages/tests/src/core/S_parseAnyAsyncInStepsWith_test.res @@ -462,7 +462,7 @@ module Tuple = { } module Union = { - asyncTest("[Union] Successfully parses", t => { + Failing.asyncTest("[Union] Successfully parses", t => { let schema = S.union([S.literal(1), S.literal(2)->validAsyncRefine, S.literal(3)]) Promise.all([ @@ -478,39 +478,56 @@ module Union = { ])->Promise.thenResolve(_ => ()) }) - asyncTest("[Union] Doesn't return sync error when fails to parse sync part of async item", t => { + test("[Union] Fails to parse since it's not supported", t => { let schema = S.union([S.literal(1), S.literal(2)->validAsyncRefine, S.literal(3)]) - let input = %raw("true") - (input->S.parseAnyAsyncInStepsWith(schema)->Result.getExn)()->Promise.thenResolve(result => { - t->U.assertErrorResult( - result, - { - code: InvalidUnion([ - U.error({ - code: InvalidLiteral({expected: S.Literal.parse(1.), received: input}), - path: S.Path.empty, - operation: Parsing, - }), - U.error({ - code: InvalidLiteral({expected: S.Literal.parse(2.), received: input}), - path: S.Path.empty, - operation: Parsing, - }), - U.error({ - code: InvalidLiteral({expected: S.Literal.parse(3.), received: input}), - path: S.Path.empty, - operation: Parsing, - }), - ]), - operation: Parsing, - path: S.Path.empty, - }, - ) - }) + t->Assert.throws( + () => { + 1->S.parseAnyOrRaiseWith(schema) + }, + ~expectations={ + message: "Failed parsing at root. Reason: S.union doesn\'t support async items. Please create an issue to rescript-schema if you nead the feature.", + }, + (), + ) }) - test("[Union] Parses async items in parallel", t => { + Failing.asyncTest( + "[Union] Doesn't return sync error when fails to parse sync part of async item", + t => { + let schema = S.union([S.literal(1), S.literal(2)->validAsyncRefine, S.literal(3)]) + let input = %raw("true") + + (input->S.parseAnyAsyncInStepsWith(schema)->Result.getExn)()->Promise.thenResolve(result => { + t->U.assertErrorResult( + result, + { + code: InvalidUnion([ + U.error({ + code: InvalidLiteral({expected: S.Literal.parse(1.), received: input}), + path: S.Path.empty, + operation: Parsing, + }), + U.error({ + code: InvalidLiteral({expected: S.Literal.parse(2.), received: input}), + path: S.Path.empty, + operation: Parsing, + }), + U.error({ + code: InvalidLiteral({expected: S.Literal.parse(3.), received: input}), + path: S.Path.empty, + operation: Parsing, + }), + ]), + operation: Parsing, + path: S.Path.empty, + }, + ) + }) + }, + ) + + Failing.test("[Union] Parses async items in parallel", t => { let actionCounter = ref(0) let schema = S.union([ @@ -850,6 +867,12 @@ module Json = { asyncTest("[JsonString] Successfully parses", t => { let schema = S.jsonString(S.int->validAsyncRefine) + t->U.assertCompiledCode( + ~schema, + ~op=#parse, + `i=>{if(typeof i!=="string"){e[3](i)}let v0;try{v0=JSON.parse(i)}catch(t){e[0](t.message)}if(typeof v0!=="number"||v0>2147483647||v0<-2147483648||v0%1!==0){e[1](v0)}return e[2](v0)}`, + ) + ("1"->S.parseAnyAsyncInStepsWith(schema)->Result.getExn)()->Promise.thenResolve(result => { t->Assert.deepEqual(result, Ok(1), ()) }) diff --git a/packages/tests/src/core/S_preprocess_test.res b/packages/tests/src/core/S_preprocess_test.res index 31b6e616..cb889361 100644 --- a/packages/tests/src/core/S_preprocess_test.res +++ b/packages/tests/src/core/S_preprocess_test.res @@ -147,6 +147,13 @@ asyncTest("Can apply other actions after async preprocess", t => { ->S.String.trim ->S.preprocess(_ => {asyncParser: value => () => Promise.resolve(value)}) + // TODO: Can improve builder to use string schema and trim without .then in between + t->U.assertCompiledCode( + ~schema, + ~op=#parse, + `i=>{let v5=e[0](i);return ()=>v5().then(v0=>{let v2=e[1](v0),v4=()=>v2().then(v1=>{if(typeof v1!=="string"){e[2](v1)}return v1});return (()=>v4().then(v3=>{return e[3](v3)}))()})}`, + ) + %raw(`" Hello world!"`) ->S.parseAsyncWith(schema) ->Promise.thenResolve(result => { @@ -257,7 +264,7 @@ test("Compiled parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v0;v0=e[0](i);if(typeof v0!=="number"||v0>2147483647||v0<-2147483648||v0%1!==0){e[1](v0)}return v0}`, + `i=>{let v0=e[0](i);if(typeof v0!=="number"||v0>2147483647||v0<-2147483648||v0%1!==0){e[1](v0)}return v0}`, ) }) @@ -270,7 +277,24 @@ test("Compiled async parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{let v0,v1;v0=e[0](i);v1=()=>v0().then(v2=>{if(typeof v2!=="number"||v2>2147483647||v2<-2147483648||v2%1!==0){e[1](v2)}return v2});return v1}`, + `i=>{let v1=e[0](i);return ()=>v1().then(v0=>{if(typeof v0!=="number"||v0>2147483647||v0<-2147483648||v0%1!==0){e[1](v0)}return v0})}`, + ) +}) + +test("Compiled async parse code snapshot for object", t => { + let schema = S.object(s => + { + "foo": s.field("foo", S.string), + } + )->S.preprocess(_ => { + asyncParser: _ => () => 1->Int.toFloat->Promise.resolve, + serializer: _ => 1.->Int.fromFloat, + }) + + t->U.assertCompiledCode( + ~schema, + ~op=#parse, + `i=>{let v2=e[0](i);return ()=>v2().then(v0=>{if(!v0||v0.constructor!==Object){e[1](v0)}let v1=v0["foo"];if(typeof v1!=="string"){e[2](v1)}return {"foo":v1,}})}`, ) }) diff --git a/packages/tests/src/core/S_refine_test.res b/packages/tests/src/core/S_refine_test.res index 7e6a935b..be9e4bf5 100644 --- a/packages/tests/src/core/S_refine_test.res +++ b/packages/tests/src/core/S_refine_test.res @@ -50,6 +50,27 @@ test("Successfully refines on serializing", t => { ) }) +test("Successfully parses simple object with empty refine", t => { + let schema = S.object(s => + { + "foo": s.field("foo", S.string), + "bar": s.field("bar", S.bool), + } + )->S.refine(_ => _ => ()) + + t->Assert.deepEqual( + %raw(`{ + "foo": "string", + "bar": true, + }`)->S.parseAnyWith(schema), + Ok({ + "foo": "string", + "bar": true, + }), + (), + ) +}) + test("Compiled parse code snapshot for simple object with refine", t => { let schema = S.object(s => { @@ -61,6 +82,7 @@ test("Compiled parse code snapshot for simple object with refine", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!i||i.constructor!==Object){e[3](i)}let v0,v1,v2;v0=i["foo"];if(typeof v0!=="string"){e[0](v0)}v1=i["bar"];if(typeof v1!=="boolean"){e[1](v1)}v2={"foo":v0,"bar":v1,};e[2](v2);return v2}`, + // TODO: Double "let" looks wrong + `i=>{if(!i||i.constructor!==Object){e[3](i)}let v2;let v0=i["foo"],v1=i["bar"];if(typeof v0!=="string"){e[0](v0)}if(typeof v1!=="boolean"){e[1](v1)}v2={"foo":v0,"bar":v1,};e[2](v2);return v2}`, ) }) diff --git a/packages/tests/src/core/S_transform_test.res b/packages/tests/src/core/S_transform_test.res index 675cf6d5..639bc305 100644 --- a/packages/tests/src/core/S_transform_test.res +++ b/packages/tests/src/core/S_transform_test.res @@ -293,6 +293,12 @@ asyncTest("Can apply other actions after async transform", t => { ->S.String.trim ->S.transform(_ => {asyncParser: value => () => Promise.resolve(value)}) + t->U.assertCompiledCode( + ~schema, + ~op=#parse, + `i=>{if(typeof i!=="string"){e[3](i)}let v1=e[0](i),v3=()=>v1().then(v0=>{return e[1](v0)});return ()=>v3().then(v2=>{return (e[2](v2))()})}`, + ) + %raw(`" Hello world!"`) ->S.parseAsyncWith(schema) ->Promise.thenResolve(result => { @@ -322,7 +328,7 @@ test("Compiled async parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(typeof i!=="number"||i>2147483647||i<-2147483648||i%1!==0){e[1](i)}let v0;v0=e[0](i);return v0}`, + `i=>{if(typeof i!=="number"||i>2147483647||i<-2147483648||i%1!==0){e[1](i)}return e[0](i)}`, ) }) diff --git a/packages/tests/src/core/S_tuple_test.res b/packages/tests/src/core/S_tuple_test.res index ab52b3d4..4d0978c2 100644 --- a/packages/tests/src/core/S_tuple_test.res +++ b/packages/tests/src/core/S_tuple_test.res @@ -234,7 +234,7 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!Array.isArray(i)){e[3](i)}let v0,v1;if(i.length!==2){e[0](i.length)}v0=i["0"];if(typeof v0!=="string"){e[1](v0)}v1=i["1"];if(typeof v1!=="boolean"){e[2](v1)}return [v0,v1,]}`, + `i=>{if(!Array.isArray(i)){e[3](i)}if(i.length!==2){e[0](i.length)}let v0=i["0"],v1=i["1"];if(typeof v0!=="string"){e[1](v0)}if(typeof v1!=="boolean"){e[2](v1)}return [v0,v1,]}`, ) }) @@ -247,7 +247,7 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!Array.isArray(i)){e[3](i)}let v0,v1,v2;if(i.length!==2){e[0](i.length)}v0=e[1](i["0"]);v1=i["1"];if(typeof v1!=="boolean"){e[2](v1)}v2=()=>Promise.all([v0()]).then(([v0])=>([v0,v1,]));return v2}`, + `i=>{if(!Array.isArray(i)){e[3](i)}if(i.length!==2){e[0](i.length)}let v0,v1=i["1"];v0=e[1](i["0"]);if(typeof v1!=="boolean"){e[2](v1)}return ()=>Promise.all([v0()]).then(([v0])=>([v0,v1,]))}`, ) }) @@ -258,10 +258,17 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0;v0=[];v0["0"]=i["0"];v0["1"]=i["1"];return v0}`, + `i=>{let v0=[];v0["0"]=i["0"];v0["1"]=i["1"];return v0}`, ) }) + test("Compiled serialize code snapshot for empty tuple", t => { + let schema = S.tuple(_ => ()) + + // TODO: No need to do unit check ? + t->U.assertCompiledCode(~schema, ~op=#serialize, `i=>{if(i!==e[0]){e[1](i)}return []}`) + }) + test( "Compiled parse code snapshot for simple tuple with transformation, constants and discriminants", t => { @@ -277,7 +284,7 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#parse, - `i=>{if(!Array.isArray(i)){e[5](i)}let v0,v1,v2;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],}}`, + `i=>{if(!Array.isArray(i)){e[5](i)}if(i.length!==3){e[0](i.length)}let v0=i["1"],v1=i["2"],v2=i["0"];v2===0||e[4](v2);if(typeof v0!=="string"){e[1](v0)}if(typeof v1!=="boolean"){e[2](v1)}return {"foo":v0,"bar":v1,"zoo":e[3],}}`, ) }, ) @@ -297,7 +304,7 @@ module Compiled = { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0;v0=[];if(i["zoo"]!==e[0]){e[1](i["zoo"])}v0["1"]=i["foo"];v0["2"]=i["bar"];v0["0"]=e[2];return v0}`, + `i=>{let v0=[];if(i["zoo"]!==e[0]){e[1](i["zoo"])}v0["1"]=i["foo"];v0["2"]=i["bar"];v0["0"]=e[2];return v0}`, ) }, ) diff --git a/packages/tests/src/core/S_union_test.res b/packages/tests/src/core/S_union_test.res index 195296eb..2ad2dd55 100644 --- a/packages/tests/src/core/S_union_test.res +++ b/packages/tests/src/core/S_union_test.res @@ -25,6 +25,64 @@ test("Successfully parses polymorphic variants", t => { t->Assert.deepEqual(%raw(`"apple"`)->S.parseAnyWith(schema), Ok(#apple), ()) }) +test("Parses when both schemas misses parser", t => { + let schema = S.union([ + S.literal(#apple)->S.transform(_ => {serializer: _ => #apple}), + S.string->S.transform(_ => {serializer: _ => "apple"}), + ]) + + t->U.assertErrorResult( + %raw(`null`)->S.parseAnyWith(schema), + { + code: InvalidUnion([ + U.error({ + code: InvalidOperation({description: "The S.transform parser is missing"}), + operation: Parsing, + path: S.Path.empty, + }), + U.error({ + code: InvalidOperation({description: "The S.transform parser is missing"}), + operation: Parsing, + path: S.Path.empty, + }), + ]), + operation: Parsing, + path: S.Path.empty, + }, + ) + + t->U.assertCompiledCode(~schema, ~op=#parse, `i=>{e[4]([e[1],e[3],]);return i}`) +}) + +test("Serializes when both schemas misses serializer", t => { + let schema = S.union([ + S.literal(#apple)->S.transform(_ => {parser: _ => #apple}), + S.string->S.transform(_ => {parser: _ => #apple}), + ]) + + t->U.assertErrorResult( + %raw(`null`)->S.serializeWith(schema), + { + code: InvalidUnion([ + U.error({ + code: InvalidOperation({description: "The S.transform serializer is missing"}), + operation: Serializing, + path: S.Path.empty, + }), + U.error({ + code: InvalidOperation({description: "The S.transform serializer is missing"}), + operation: Serializing, + path: S.Path.empty, + }), + ]), + operation: Serializing, + path: S.Path.empty, + }, + ) + + t->U.assertCompiledCode(~schema, ~op=#serialize, `i=>{e[2]([e[0],e[1],]);return i}`) +}) + test("Parses when second struct misses parser", t => { let schema = S.union([S.literal(#apple), S.string->S.transform(_ => {serializer: _ => "apple"})]) @@ -33,7 +91,7 @@ test("Parses when second struct misses parser", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `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}`, + `i=>{let v0;try{i==="apple"||e[0](i);v0=i}catch(e0){e[3]([e0,e[2],])}return v0}`, ) }) @@ -45,7 +103,7 @@ test("Serializes when second struct misses serializer", t => { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `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}`, + `i=>{let v0;try{i==="apple"||e[0](i);v0=i}catch(e0){e[2]([e0,e[1],])}return v0}`, ) }) @@ -339,11 +397,20 @@ test("Compiled parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `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}`, + `i=>{let v0;try{i===0||e[0](i);v0=i}catch(e0){try{i===1||e[1](i);v0=i}catch(e1){e[2]([e0,e1,])}}return v0}`, + ) +}) + +test("Compiled parse code snapshot for discriminated union", t => { + t->U.assertCompiledCode( + ~schema=Advanced.shapeSchema, + ~op=#parse, + `i=>{let v2;try{if(!i||i.constructor!==Object){e[0](i)}let v0=i["radius"],v1=i["kind"];v1==="circle"||e[3](v1);if(typeof v0!=="number"||Number.isNaN(v0)){e[2](v0)}v2={"TAG":e[1],"radius":v0,}}catch(e0){try{if(!i||i.constructor!==Object){e[4](i)}let v3=i["x"],v4=i["kind"];v4==="square"||e[7](v4);if(typeof v3!=="number"||Number.isNaN(v3)){e[6](v3)}v2={"TAG":e[5],"x":v3,}}catch(e1){try{if(!i||i.constructor!==Object){e[8](i)}let v5=i["x"],v6=i["y"],v7=i["kind"];v7==="triangle"||e[12](v7);if(typeof v5!=="number"||Number.isNaN(v5)){e[10](v5)}if(typeof v6!=="number"||Number.isNaN(v6)){e[11](v6)}v2={"TAG":e[9],"x":v5,"y":v6,}}catch(e2){e[13]([e0,e1,e2,])}}}return v2}`, ) }) -test("Compiled async parse code snapshot", t => { +// It shouldn't compile since it throw InvalidOperation error +Failing.test("Compiled async parse code snapshot", t => { let schema = S.union([ S.literal(0)->S.transform(_ => {asyncParser: i => () => Promise.resolve(i)}), S.literal(1), @@ -352,18 +419,18 @@ test("Compiled async parse code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#parse, - `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}`, + `i=>{let v0=e[1](i),v1;try{i===0||e[0](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}`, ) }) test("Compiled serialize code snapshot", t => { let schema = S.union([S.literal(0), S.literal(1)]) - // TODO: Improve compiled code + // TODO: Improve compiled code for literals t->U.assertCompiledCode( ~schema, ~op=#serialize, - `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}`, + `i=>{let v0;try{i===0||e[0](i);v0=i}catch(e0){try{i===1||e[1](i);v0=i}catch(e1){e[2]([e0,e1,])}}return v0}`, ) }) @@ -381,6 +448,6 @@ test("Compiled serialize code snapshot for unboxed variant", t => { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0;try{if(typeof i!=="string"){e[0](i)}v0=i}catch(v1){if(v1&&v1.s===s){try{let v3;v3=e[1](i);if(typeof v3!=="string"){e[2](v3)}v0=v3}catch(v2){if(v2&&v2.s===s){e[3]([v1,v2,])}else{throw v2}}}else{throw v1}}return v0}`, + `i=>{let v0,v1=e[1](i);try{if(typeof i!=="string"){e[0](i)}v0=i}catch(e0){try{if(typeof v1!=="string"){e[2](v1)}v0=v1}catch(e1){e[3]([e0,e1,])}}return v0}`, ) }) diff --git a/packages/tests/src/core/S_variant_test.res b/packages/tests/src/core/S_variant_test.res index e4bb53e7..68ac8cc4 100644 --- a/packages/tests/src/core/S_variant_test.res +++ b/packages/tests/src/core/S_variant_test.res @@ -122,7 +122,7 @@ test("Compiled serialize code snapshot", t => { t->U.assertCompiledCode( ~schema, ~op=#serialize, - `i=>{let v0;v0=i["TAG"];if(v0!==e[0]){e[1](v0)}return i["_0"]}`, + `i=>{let v0=i["TAG"];if(v0!==e[0]){e[1](v0)}return i["_0"]}`, ) }) @@ -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===12||e[4](v4);v2["1"]=v4;return v2}`, + `i=>{let v0=e[2],v1=v0["0"],v2=[],v3=v0["1"];if(i!==e[0]){e[1](i)}v1===true||e[3](v1);v2["0"]=v1;v3===12||e[4](v3);v2["1"]=v3;return v2}`, ) }, ) diff --git a/src/S_Core.bs.mjs b/src/S_Core.bs.mjs index c1019edf..e5f47cc3 100644 --- a/src/S_Core.bs.mjs +++ b/src/S_Core.bs.mjs @@ -103,72 +103,161 @@ function classify(schema) { return schema.t; } -function scope(b, fn) { - var prevVarsAllocation = b.l; - var prevCode = b.c; - b.l = ""; - b.c = ""; - var resultCode = fn(b); +function scope(b) { + return { + c: "", + o: b.o, + v: b.v, + l: "", + a: false, + p: b, + e: b.e + }; +} + +function allocateScope(b) { var varsAllocation = b.l; - var code = varsAllocation === "" ? b.c : "let " + varsAllocation + ";" + b.c; - b.l = prevVarsAllocation; - b.c = prevCode; - return code + resultCode; + b.a = true; + b.p.v = b.v; + if (varsAllocation === "") { + return b.c; + } else { + return "let " + varsAllocation + ";" + b.c; + } +} + +function exitScope(b) { + var parent = b.p; + parent.c = parent.c + allocateScope(b); + return parent; } function varWithoutAllocation(b) { var newCounter = b.v + 1; b.v = newCounter; - var v = "v" + newCounter; - b.s.add(v); - return v; + return "v" + newCounter; } -function $$var(b) { - var v = varWithoutAllocation(b); - b.l = b.l === "" ? v : b.l + "," + v; - return v; +function allocateVal(b) { + return { + s: b, + a: false + }; +} + +function val(b, initial) { + return { + i: initial, + s: b, + a: false + }; } -function toVar(b, val) { - if (b.s.has(val)) { - return val; +function asyncVal(b, initial) { + return { + i: initial, + s: b, + a: true + }; +} + +function inline(_b, val) { + var $$var = val.v; + if ($$var !== undefined) { + return $$var; + } else { + return val.i; + } +} + +function $$var(b, val) { + var _var = val.v; + if (_var !== undefined) { + return _var; + } + var $$var$1 = varWithoutAllocation(b); + var isValScopeActive = !val.s.a; + var activeScope = isValScopeActive ? val.s : b; + var i = val.i; + var allocation = i !== undefined && isValScopeActive ? $$var$1 + "=" + i : $$var$1; + var varsAllocation = activeScope.l; + activeScope.l = varsAllocation === "" ? allocation : varsAllocation + "," + allocation; + var i$1 = val.i; + if (i$1 !== undefined && !isValScopeActive) { + b.c = b.c + ($$var$1 + "=" + i$1 + ";"); } - var $$var$1 = $$var(b); - b.c = b.c + ($$var$1 + "=" + val + ";"); + val.v = $$var$1; return $$var$1; } -function transform(b, input, isAsync, operation) { - if (b.a === true) { - var prevCode = b.c; - b.c = ""; - var inputVar = varWithoutAllocation(b); - var operationOutputVar = operation(b, inputVar); - var outputVar = $$var(b); - b.c = prevCode + (outputVar + "=()=>" + input + "().then(" + inputVar + "=>{" + b.c + "return " + operationOutputVar + ( - isAsync ? "()" : "" - ) + "});"); - return outputVar; +function push(b, input, val) { + return $$var(b, input) + ".push(" + inline(b, val) + ")"; +} + +function addKey(b, input, key, val) { + return $$var(b, input) + "[" + key + "]=" + inline(b, val); +} + +function set(b, input, val) { + if (input === val) { + return ""; + } + var match = input.a; + if (match) { + var match$1 = val.a; + if (!match$1) { + return $$var(b, input) + "=()=>Promise.resolve(" + inline(b, val) + ")"; + } + + } else { + var match$2 = val.a; + if (match$2) { + input.a = true; + return $$var(b, input) + "=" + inline(b, val); + } + } - if (!isAsync) { - return operation(b, input); + return $$var(b, input) + "=" + inline(b, val); +} + +function setInlined(b, input, inlined) { + return $$var(b, input) + "=" + inlined; +} + +function map(b, inlinedFn, input) { + return val(b, inlinedFn + "(" + inline(b, input) + ")"); +} + +function transform(b, input, operation) { + if (!input.a) { + return operation(input); } - b.a = true; - var outputVar$1 = $$var(b); - b.c = b.c + (outputVar$1 + "=" + operation(b, input) + ";"); - return outputVar$1; + var prevCode = b.c; + b.c = ""; + var operationInput = { + v: varWithoutAllocation(b), + s: b, + a: false + }; + var operationOutputVal = operation(operationInput); + var operationCode = b.c; + b.c = prevCode; + return asyncVal(b, "()=>" + $$var(b, input) + "().then(" + $$var(b, operationInput) + "=>{" + operationCode + "return " + ( + operationOutputVal.a ? "(" + inline(b, operationOutputVal) + ")()" : inline(b, operationOutputVal) + ) + "})"); } function embedSyncOperation(b, input, fn) { - return transform(b, input, false, (function (b, input) { - return "e[" + (b.e.push(fn) - 1) + "](" + input + ")"; + return transform(b, input, (function (input) { + return map(b, "e[" + (b.e.push(fn) - 1) + "]", input); })); } function embedAsyncOperation(b, input, fn) { - return transform(b, input, true, (function (b, input) { - return "e[" + (b.e.push(fn) - 1) + "](" + input + ")"; + return transform(b, input, (function (input) { + var val = map(b, "e[" + (b.e.push(fn) - 1) + "]", input); + val.a = true; + return val; })); } @@ -199,30 +288,29 @@ function withCatch(b, $$catch, fn) { var prevCode = b.c; b.c = ""; var errorVar = varWithoutAllocation(b); - var maybeResolveVar = $$catch(b, errorVar); + var maybeResolveVal = $$catch(b, errorVar); var catchCode = "if(" + (errorVar + "&&" + errorVar + ".s===s") + "){" + b.c; b.c = ""; - var fnOutput = fn(b); - var isAsync = b.a; - var isInlined = !b.s.has(fnOutput); - var outputVar = isAsync || isInlined ? $$var(b) : fnOutput; - var catchCode$1 = maybeResolveVar !== undefined ? (function (catchLocation) { + var b$1 = scope(b); + var fnOutput = fn(b$1); + var b$2 = exitScope(b$1); + var isAsync = fnOutput.a; + var output = allocateVal(b$2); + output.a = isAsync; + var catchCode$1 = maybeResolveVal !== undefined ? (function (catchLocation) { return catchCode + ( - catchLocation === 1 ? "return Promise.resolve(" + maybeResolveVar + ")" : ( - catchLocation === 2 ? "return " + maybeResolveVar : ( - isAsync ? outputVar + "=()=>Promise.resolve(" + maybeResolveVar + ")" : outputVar + "=" + maybeResolveVar - ) + catchLocation === 1 ? "return Promise.resolve(" + inline(b$2, maybeResolveVal) + ")" : ( + catchLocation === 2 ? "return " + inline(b$2, maybeResolveVal) : set(b$2, output, maybeResolveVal) ) ) + ("}else{throw " + errorVar + "}"); }) : (function (param) { return catchCode + "}throw " + errorVar; }); - b.c = prevCode + ("try{" + b.c + ( - isAsync ? outputVar + "=()=>{try{return " + fnOutput + "().catch(" + errorVar + "=>{" + catchCode$1(2) + "})}catch(" + errorVar + "){" + catchCode$1(1) + "}};" : ( - isInlined ? outputVar + "=" + fnOutput : "" - ) + var fnOutputVar = $$var(b$2, fnOutput); + b$2.c = prevCode + ("try{" + b$2.c + ( + isAsync ? setInlined(b$2, output, "()=>{try{return " + fnOutputVar + "().catch(" + errorVar + "=>{" + catchCode$1(2) + "})}catch(" + errorVar + "){" + catchCode$1(1) + "}}") : set(b$2, output, fnOutput) ) + "}catch(" + errorVar + "){" + catchCode$1(0) + "}"); - return outputVar; + return output; } function withPathPrepend(b, path, maybeDynamicLocationVar, fn) { @@ -248,7 +336,8 @@ function withPathPrepend(b, path, maybeDynamicLocationVar, fn) { } } -function typeFilterCode(b, typeFilter, schema, inputVar, path) { +function typeFilterCode(b, typeFilter, schema, input, path) { + var inputVar = $$var(b, input); return "if(" + typeFilter(inputVar) + "){" + raiseWithArg(b, path, (function (input) { return { TAG: "InvalidType", @@ -259,47 +348,25 @@ function typeFilterCode(b, typeFilter, schema, inputVar, path) { } function use(b, schema, input, path) { - var isParentAsync = b.a; var isParsing = b.o === "Parsing"; - b.i = input; - b.a = false; - var output = ( - isParsing ? schema.p : schema.s - )(b, schema, path); - if (isParsing) { - schema.i = b.a; - b.a = isParentAsync || b.a; - } - return output; + return ( + isParsing ? schema.p : schema.s + )(b, input, schema, path); } function useWithTypeFilter(b, schema, input, path) { var typeFilter = schema.f; - var input$1; if (typeFilter !== undefined) { - var inputVar = toVar(b, input); - b.c = b.c + typeFilterCode(b, typeFilter, schema, inputVar, path); - input$1 = inputVar; - } else { - input$1 = input; + b.c = b.c + typeFilterCode(b, typeFilter, schema, input, path); } - return use(b, schema, input$1, path); + var b$1 = scope(b); + var val = use(b$1, schema, input, path); + exitScope(b$1); + return val; } -function withBuildErrorInline(b, fn) { - try { - return fn(); - } - catch (raw_exn){ - var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); - var error = getOrRethrow(exn); - b.c = "throw " + ("e[" + (b.e.push(error) - 1) + "]") + ";"; - return b.i; - } -} - -function noop(b, param, param$1) { - return b.i; +function noop(_b, input, param, param$1) { + return input; } function noopOperation(i) { @@ -308,30 +375,34 @@ function noopOperation(i) { function build(builder, schema, operation) { var b = { - a: false, c: "", o: operation, v: -1, - s: new Set(["i"]), l: "", - i: "i", + a: false, + p: (void 0), e: [] }; - var output = builder(b, schema, ""); + var input = { + v: "i", + s: b, + a: false + }; + var output = builder(b, input, schema, ""); if (b.l !== "") { b.c = "let " + b.l + ";" + b.c; } if (operation === "Parsing") { var typeFilter = schema.f; if (typeFilter !== undefined) { - b.c = typeFilterCode(b, typeFilter, schema, "i", "") + b.c; + b.c = typeFilterCode(b, typeFilter, schema, input, "") + b.c; } - schema.i = b.a; + schema.i = output.a; } - if (b.c === "" && output === "i") { + if (b.c === "" && output === input) { return noopOperation; } - var inlinedFunction = "i=>{" + b.c + "return " + output + "}"; + var inlinedFunction = "i=>{" + b.c + "return " + inline(b, output) + "}"; return new Function("e", "s", "return " + inlinedFunction)(b.e, symbol); } @@ -378,15 +449,11 @@ function strictEqualCheckBuilder(b, inputVar, literal) { var undefined_value = undefined; -function undefined_b(param, inputVar, param$1) { - return inputVar + "===void 0"; -} - var $$undefined = { kind: "Undefined", value: undefined_value, s: "undefined", - b: undefined_b, + b: inlinedStrictEqualCheckBuilder, j: false }; @@ -452,56 +519,9 @@ function parseInternal(value) { if (value === null) { return $$null; } else if (Array.isArray(value)) { - var items = []; - var isJsonable = true; - var string = "["; - for(var idx = 0 ,idx_finish = value.length; idx < idx_finish; ++idx){ - var itemValue = value[idx]; - var itemLiteral = parseInternal(itemValue); - if (isJsonable && !itemLiteral.j) { - isJsonable = false; - } - if (idx !== 0) { - string = string + ","; - } - string = string + itemLiteral.s; - items.push(itemLiteral); - } - return { - kind: "Array", - value: value, - s: string + "]", - b: arrayCheckBuilder, - j: isJsonable, - i: Caml_option.some(items) - }; + return array(value); } else if (value.constructor === Object) { - var items$1 = {}; - var string$1 = "{"; - var isJsonable$1 = true; - var fields = Object.keys(value); - var numberOfFields = fields.length; - for(var idx$1 = 0; idx$1 < numberOfFields; ++idx$1){ - var field = fields[idx$1]; - var itemValue$1 = value[field]; - var itemLiteral$1 = parseInternal(itemValue$1); - if (isJsonable$1 && !itemLiteral$1.j) { - isJsonable$1 = false; - } - if (idx$1 !== 0) { - string$1 = string$1 + ","; - } - string$1 = string$1 + (JSON.stringify(field) + ":" + itemLiteral$1.s); - items$1[field] = itemLiteral$1; - } - return { - kind: "Dict", - value: value, - s: string$1 + "}", - b: dictCheckBuilder, - j: isJsonable$1, - i: Caml_option.some(items$1) - }; + return dict(value); } else { return { kind: "Object", @@ -536,6 +556,61 @@ function parseInternal(value) { } } +function dict(value) { + var items = {}; + var string = "{"; + var isJsonable = true; + var fields = Object.keys(value); + var numberOfFields = fields.length; + for(var idx = 0; idx < numberOfFields; ++idx){ + var field = fields[idx]; + var itemValue = value[field]; + var itemLiteral = parseInternal(itemValue); + if (isJsonable && !itemLiteral.j) { + isJsonable = false; + } + if (idx !== 0) { + string = string + ","; + } + string = string + (JSON.stringify(field) + ":" + itemLiteral.s); + items[field] = itemLiteral; + } + return { + kind: "Dict", + value: value, + s: string + "}", + b: dictCheckBuilder, + j: isJsonable, + i: Caml_option.some(items) + }; +} + +function array(value) { + var items = []; + var isJsonable = true; + var string = "["; + for(var idx = 0 ,idx_finish = value.length; idx < idx_finish; ++idx){ + var itemValue = value[idx]; + var itemLiteral = parseInternal(itemValue); + if (isJsonable && !itemLiteral.j) { + isJsonable = false; + } + if (idx !== 0) { + string = string + ","; + } + string = string + itemLiteral.s; + items.push(itemLiteral); + } + return { + kind: "Array", + value: value, + s: string + "]", + b: arrayCheckBuilder, + j: isJsonable, + i: Caml_option.some(items) + }; +} + function parse(any) { return parseInternal(any); } @@ -551,11 +626,11 @@ function loop(_schema) { case "Literal" : return literal._0; case "Object" : - return parseInternal(mapValues(literal.fields, (function (itemSchema) { + return dict(mapValues(literal.fields, (function (itemSchema) { return loop(itemSchema).value; }))); case "Tuple" : - return parseInternal(literal._0.map(function (itemSchema) { + return array(literal._0.map(function (itemSchema) { return loop(itemSchema).value; })); case "Union" : @@ -684,7 +759,7 @@ function validateJsonableSchema(_schema, rootSchema, _isRootOpt) { }; } -function unexpectedAsync(param) { +function unexpectedAsync() { throw new RescriptSchemaError("UnexpectedAsync", "Parsing", ""); } @@ -929,7 +1004,7 @@ var Id = { var empty = {}; -function set(map, id, metadata) { +function set$1(map, id, metadata) { if (map === empty) { return ({[id]:metadata}); } @@ -942,8 +1017,8 @@ function get(schema, id) { return schema.m[id]; } -function set$1(schema, id, metadata) { - var metadataMap = set(schema.m, id, metadata); +function set$2(schema, id, metadata) { + var metadataMap = set$1(schema.m, id, metadata); return { t: schema.t, n: schema.n, @@ -962,23 +1037,25 @@ function recursive(fn) { var schema = fn(placeholder); Object.assign(placeholder, schema); var builder = placeholder.p; - placeholder.p = (function (b, selfSchema, path) { - var input = b.i; + placeholder.p = (function (b, input, selfSchema, path) { selfSchema.p = noop; - var ctx = { - a: false, + var b$1 = { c: "", o: "Parsing", v: -1, - s: new Set(["i"]), l: "", - i: "i", + a: false, + p: (void 0), e: [] }; - builder(ctx, selfSchema, path); - var isAsync = ctx.a; - selfSchema.p = (function (b, selfSchema, param) { - var input = b.i; + var input$1 = { + v: "i", + s: b$1, + a: false + }; + var output = builder(b$1, input$1, selfSchema, path); + var isAsync = output.a; + selfSchema.p = (function (b, input, selfSchema, param) { if (isAsync) { return embedAsyncOperation(b, input, (function (input) { return internalParseAsyncWith(input, selfSchema); @@ -1005,10 +1082,8 @@ function recursive(fn) { })); }); var builder$1 = placeholder.s; - placeholder.s = (function (b, selfSchema, path) { - var input = b.i; - selfSchema.s = (function (b, selfSchema, param) { - var input = b.i; + placeholder.s = (function (b, input, selfSchema, path) { + selfSchema.s = (function (b, input, selfSchema, param) { return embedSyncOperation(b, input, (function (input) { return serializeToUnknownOrRaiseWith(input, selfSchema); })); @@ -1050,20 +1125,17 @@ function internalRefine(schema, refiner) { return { t: schema.t, n: schema.n, - p: (function (b, selfSchema, path) { - var input = b.i; - return transform(b, use(b, schema, input, path), false, (function (b, input) { - var inputVar = toVar(b, input); - b.c = b.c + refiner(b, inputVar, selfSchema, path); - return inputVar; + p: (function (b, input, selfSchema, path) { + return transform(b, use(b, schema, input, path), (function (input) { + var rCode = refiner(b, input, selfSchema, path); + b.c = b.c + rCode; + return input; })); }), - s: (function (b, selfSchema, path) { - var input = b.i; - return use(b, schema, transform(b, input, false, (function (b, input) { - var inputVar = toVar(b, input); - b.c = b.c + refiner(b, inputVar, selfSchema, path); - return inputVar; + s: (function (b, input, selfSchema, path) { + return use(b, schema, transform(b, input, (function (input) { + b.c = b.c + refiner(b, input, selfSchema, path); + return input; })), path); }), f: schema.f, @@ -1073,23 +1145,22 @@ function internalRefine(schema, refiner) { } function refine(schema, refiner) { - return internalRefine(schema, (function (b, inputVar, selfSchema, path) { + return internalRefine(schema, (function (b, input, selfSchema, path) { var value = refiner(make(selfSchema, path, b.o)); - return "e[" + (b.e.push(value) - 1) + "](" + inputVar + ");"; + return "e[" + (b.e.push(value) - 1) + "](" + $$var(b, input) + ");"; })); } function addRefinement(schema, metadataId, refinement, refiner) { var refinements = schema.m[metadataId]; - return internalRefine(set$1(schema, metadataId, refinements !== undefined ? refinements.concat(refinement) : [refinement]), refiner); + return internalRefine(set$2(schema, metadataId, refinements !== undefined ? refinements.concat(refinement) : [refinement]), refiner); } function transform$1(schema, transformer) { return { t: schema.t, n: schema.n, - p: (function (b, selfSchema, path) { - var input = b.i; + p: (function (b, input, selfSchema, path) { var input$1 = use(b, schema, input, path); var match = transformer(make(selfSchema, path, b.o)); var parser = match.p; @@ -1109,8 +1180,7 @@ function transform$1(schema, transformer) { return input$1; } }), - s: (function (b, selfSchema, path) { - var input = b.i; + s: (function (b, input, selfSchema, path) { var match = transformer(make(selfSchema, path, b.o)); var serializer = match.s; if (serializer !== undefined) { @@ -1148,36 +1218,26 @@ function preprocess(schema, transformer) { return { t: schema.t, n: schema.n, - p: (function (b, selfSchema, path) { - var input = b.i; + p: (function (b, input, selfSchema, path) { var match = transformer(make(selfSchema, path, b.o)); var parser = match.p; if (parser !== undefined) { if (match.a !== undefined) { return invalidOperation(b, path, "The S.preprocess doesn't allow parser and asyncParser at the same time. Remove parser in favor of asyncParser."); + } else { + return useWithTypeFilter(b, schema, embedSyncOperation(b, input, parser), path); } - var operationResultVar = $$var(b); - b.c = b.c + (operationResultVar + "=" + embedSyncOperation(b, input, parser) + ";"); - return useWithTypeFilter(b, schema, operationResultVar, path); } var asyncParser = match.a; - if (asyncParser === undefined) { + if (asyncParser !== undefined) { + return transform(b, embedAsyncOperation(b, input, asyncParser), (function (input) { + return useWithTypeFilter(b, schema, input, path); + })); + } else { return useWithTypeFilter(b, schema, input, path); } - var parseResultVar = embedAsyncOperation(b, input, asyncParser); - var outputVar = $$var(b); - var asyncResultVar = varWithoutAllocation(b); - b.c = b.c + (outputVar + "=()=>" + parseResultVar + "().then(" + asyncResultVar + "=>{" + scope(b, (function (b) { - var schemaOutputVar = useWithTypeFilter(b, schema, asyncResultVar, path); - var isAsync = schema.i; - return "return " + ( - isAsync ? schemaOutputVar + "()" : schemaOutputVar - ); - })) + "});"); - return outputVar; }), - s: (function (b, selfSchema, path) { - var input = b.i; + s: (function (b, input, selfSchema, path) { var input$1 = use(b, schema, input, path); var match = transformer(make(selfSchema, path, b.o)); var serializer = match.s; @@ -1199,8 +1259,7 @@ function custom(name, definer) { n: (function () { return name; }), - p: (function (b, selfSchema, path) { - var input = b.i; + p: (function (b, input, selfSchema, path) { var match = definer(make(selfSchema, path, b.o)); var parser = match.p; if (parser !== undefined) { @@ -1219,8 +1278,7 @@ function custom(name, definer) { return input; } }), - s: (function (b, selfSchema, path) { - var input = b.i; + s: (function (b, input, selfSchema, path) { var match = definer(make(selfSchema, path, b.o)); var serializer = match.s; if (serializer !== undefined) { @@ -1239,8 +1297,8 @@ function custom(name, definer) { function literal(value) { var literal$1 = parseInternal(value); - var operationBuilder = function (b, param, path) { - var inputVar = toVar(b, b.i); + var operationBuilder = function (b, input, param, path) { + var inputVar = $$var(b, input); b.c = b.c + (literal$1.b(b, inputVar, literal$1) + "||" + raiseWithArg(b, path, (function (input) { return { TAG: "InvalidLiteral", @@ -1248,7 +1306,7 @@ function literal(value) { received: input }; }), inputVar) + ";"); - return inputVar; + return input; }; return { t: { @@ -1282,12 +1340,11 @@ function factory(schema, definer) { return { t: schema.t, n: schema.n, - p: (function (b, param, path) { - var input = b.i; + p: (function (b, input, param, path) { return embedSyncOperation(b, use(b, schema, input, path), definer); }), - s: (function (b, selfSchema, path) { - var inputVar = toVar(b, b.i); + s: (function (b, input, selfSchema, path) { + var inputVar = $$var(b, input); var definition = definer(symbol); var definitionToOutput = function (definition, outputPath) { var kind = symbol === definition ? 2 : ( @@ -1302,20 +1359,21 @@ function factory(schema, definer) { var definition$1 = definition[key]; var maybeOutput = definitionToOutput(definition$1, outputPath + ("[" + JSON.stringify(key) + "]")); var match = maybeOutputRef; - if (typeof match !== "string") { + if (typeof match !== "object") { if (match === 0) { maybeOutputRef = maybeOutput; } - } else if (!(typeof maybeOutput !== "string" && maybeOutput === 0)) { + } else if (!(typeof maybeOutput !== "object" && maybeOutput === 0)) { maybeOutputRef = 1; } } return maybeOutputRef; case 1 : - var constantVar = $$var(b); - b.c = b.c + (constantVar + "=" + inputVar + outputPath + ";if(" + constantVar + "!==" + ("e[" + (b.e.push(definition) - 1) + "]") + "){" + raiseWithArg(b, path + outputPath, (function (input) { + var constantVal = outputPath === "" ? input : val(b, inputVar + outputPath); + var constantVar = $$var(b, constantVal); + b.c = b.c + ("if(" + constantVar + "!==" + ("e[" + (b.e.push(definition) - 1) + "]") + "){" + raiseWithArg(b, path + outputPath, (function (input) { return { TAG: "InvalidLiteral", expected: parseInternal(definition), @@ -1324,12 +1382,12 @@ function factory(schema, definer) { }), constantVar) + "}"); return 0; case 2 : - return inputVar + outputPath; + return outputPath === "" ? input : val(b, inputVar + outputPath); } }; var output = definitionToOutput(definition, ""); - if (typeof output === "string") { + if (typeof output === "object") { return use(b, schema, output, path); } if (output !== 0) { @@ -1337,7 +1395,7 @@ function factory(schema, definer) { } var literal = toInternalLiteral(selfSchema); if (literal !== undefined) { - return use(b, schema, "e[" + (b.e.push(literal.value) - 1) + "]", path); + return use(b, schema, val(b, "e[" + (b.e.push(literal.value) - 1) + "]"), path); } else { return invalidOperation(b, path, "Can't create serializer. The S.variant's value is not registered and not a literal. Use S.transform instead"); } @@ -1354,35 +1412,40 @@ function $$default(schema) { return schema.m[defaultMetadataId]; } -function parseOperationBuilder(b, selfSchema, path) { - var inputVar = toVar(b, b.i); - var outputVar = $$var(b); +function parseOperationBuilder(b, input, selfSchema, path) { var isNull = (selfSchema.t.TAG === "Null"); var childSchema = selfSchema.t._0; - var ifCode = scope(b, (function (b) { - return outputVar + "=" + use(b, childSchema, inputVar, path); - })); - var isAsync = childSchema.i; - b.c = b.c + ("if(" + inputVar + "!==" + ( - isNull ? "null" : "void 0" - ) + "){" + ifCode + "}else{" + outputVar + "=" + ( - isAsync ? "()=>Promise.resolve(void 0)" : "void 0" - ) + "}"); - return outputVar; -} - -function serializeOperationBuilder(b, selfSchema, path) { - var inputVar = toVar(b, b.i); - var outputVar = $$var(b); + var bb = scope(b); + var itemOutput = use(bb, childSchema, input, path); + var itemCode = allocateScope(bb); + var isTransformed = isNull || itemOutput !== input; + var output = isTransformed ? ({ + s: b, + a: itemOutput.a + }) : input; + if (itemCode !== "" || isTransformed) { + b.c = b.c + ("if(" + $$var(b, input) + "!==" + ( + isNull ? "null" : "void 0" + ) + "){" + itemCode + set(b, output, itemOutput) + "}" + ( + isNull || output.a ? "else{" + set(b, output, val(b, "void 0")) + "}" : "" + )); + } + return output; +} + +function serializeOperationBuilder(b, input, selfSchema, path) { + var output = allocateVal(b); + var inputVar = $$var(b, input); var isNull = (selfSchema.t.TAG === "Null"); var childSchema = selfSchema.t._0; - b.c = b.c + ("if(" + inputVar + "!==void 0){" + scope(b, (function (b) { - var value = Caml_option.valFromOption; - return outputVar + "=" + use(b, childSchema, "e[" + (b.e.push(value) - 1) + "](" + inputVar + ")", path); - })) + "}else{" + outputVar + "=" + ( - isNull ? "null" : "void 0" - ) + "}"); - return outputVar; + var bb = scope(b); + var value = Caml_option.valFromOption; + var itemOutput = use(bb, childSchema, map(bb, "e[" + (bb.e.push(value) - 1) + "]", input), path); + var itemCode = allocateScope(bb); + b.c = b.c + ("if(" + inputVar + "!==void 0){" + itemCode + set(b, output, itemOutput) + "}" + ( + isNull ? "else{" + setInlined(b, output, "null") + "}" : "" + )); + return output; } function maybeTypeFilter(schema, inlinedNoneValue) { @@ -1414,18 +1477,18 @@ function getWithDefault(schema, $$default) { return { t: schema.t, n: schema.n, - p: (function (b, param, path) { - var input = b.i; - return transform(b, use(b, schema, input, path), false, (function (b, input) { + p: (function (b, input, param, path) { + return transform(b, use(b, schema, input, path), (function (input) { + var inputVar = $$var(b, input); var tmp; tmp = $$default.TAG === "Value" ? "e[" + (b.e.push($$default._0) - 1) + "]" : "e[" + (b.e.push($$default._0) - 1) + "]()"; - return input + "===void 0?" + tmp + ":" + input; + return val(b, inputVar + "===void 0?" + tmp + ":" + inputVar); })); }), s: schema.s, f: schema.f, i: 0, - m: set(schema.m, defaultMetadataId, $$default) + m: set$1(schema.m, defaultMetadataId, $$default) }; } @@ -1471,16 +1534,19 @@ function noopRefinement(_b, param, param$1, param$2) { } function makeParseOperationBuilder(itemDefinitions, itemDefinitionsSet, definition, inputRefinement, unknownKeysRefinement) { - return function (b, selfSchema, path) { - var inputVar = toVar(b, b.i); + return function (b, input, selfSchema, path) { + if (inputRefinement !== undefined) { + inputRefinement(b, input, selfSchema, path); + } var registeredDefinitions = new Set(); var asyncOutputVars = []; - inputRefinement(b, selfSchema, inputVar, path); - var prevCode = b.c; - b.c = ""; - unknownKeysRefinement(b, selfSchema, inputVar, path); - var unknownKeysRefinementCode = b.c; - b.c = ""; + var b$1 = scope(b); + var inputVar = $$var(b$1, input); + var prevCode = b$1.c; + b$1.c = ""; + unknownKeysRefinement(b$1, input, selfSchema, path); + var unknownKeysRefinementCode = b$1.c; + b$1.c = ""; var definitionToOutput = function (definition, outputPath) { var kind = toKindWithSet(definition, itemDefinitionsSet); switch (kind) { @@ -1500,47 +1566,42 @@ function makeParseOperationBuilder(itemDefinitions, itemDefinitionsSet, definiti isArray ? "]" : "}" ); case 1 : - return "e[" + (b.e.push(definition) - 1) + "]"; + return "e[" + (b$1.e.push(definition) - 1) + "]"; case 2 : registeredDefinitions.add(definition); var inputPath = definition.p; - var schema = definition.s; - var fieldOuputVar = useWithTypeFilter(b, schema, inputVar + inputPath, path + inputPath); - var isAsyncField = schema.i; - if (isAsyncField) { - asyncOutputVars.push(fieldOuputVar); + var fieldOuput = useWithTypeFilter(b$1, definition.s, val(b$1, inputVar + inputPath), path + inputPath); + if (!fieldOuput.a) { + return inline(b$1, fieldOuput); } - return fieldOuputVar; + var asyncOutputVar = $$var(b$1, fieldOuput); + asyncOutputVars.push(asyncOutputVar); + return asyncOutputVar; } }; var syncOutput = definitionToOutput(definition, ""); - var registeredFieldsCode = b.c; - b.c = ""; + var registeredFieldsCode = b$1.c; + b$1.c = ""; for(var idx = 0 ,idx_finish = itemDefinitions.length; idx < idx_finish; ++idx){ var itemDefinition = itemDefinitions[idx]; if (!registeredDefinitions.has(itemDefinition)) { var inputPath = itemDefinition.p; - var schema = itemDefinition.s; - var fieldOuputVar = useWithTypeFilter(b, schema, inputVar + inputPath, path + inputPath); - var isAsyncField = schema.i; - if (isAsyncField) { - asyncOutputVars.push(fieldOuputVar); + var fieldOuput = useWithTypeFilter(b$1, itemDefinition.s, val(b$1, inputVar + inputPath), path + inputPath); + if (fieldOuput.a) { + asyncOutputVars.push($$var(b$1, fieldOuput)); } } } - var unregisteredFieldsCode = b.c; - b.c = prevCode + unregisteredFieldsCode + registeredFieldsCode + unknownKeysRefinementCode; - if (asyncOutputVars.length === 0) { - return syncOutput; - } - var outputVar = $$var(b); - b.c = b.c + (outputVar + "=()=>Promise.all([" + asyncOutputVars.map(function (asyncOutputVar) { - return asyncOutputVar + "()"; - }).join(",") + "]).then(([" + asyncOutputVars.toString() + "])=>(" + syncOutput + "));"); - return outputVar; + var unregisteredFieldsCode = b$1.c; + b$1.c = prevCode + unregisteredFieldsCode + registeredFieldsCode + unknownKeysRefinementCode; + var val$1 = asyncOutputVars.length === 0 ? val(b$1, syncOutput) : asyncVal(b$1, "()=>Promise.all([" + asyncOutputVars.map(function (asyncOutputVar) { + return asyncOutputVar + "()"; + }).join(",") + "]).then(([" + asyncOutputVars.toString() + "])=>(" + syncOutput + "))"); + exitScope(b$1); + return val$1; }; } @@ -1599,13 +1660,15 @@ function factory$3(definer) { return JSON.stringify(fieldName) + ": " + fieldSchema.n(); }).join(", ") + "})"; }), - p: makeParseOperationBuilder(itemDefinitions, itemDefinitionsSet$1, definition, noopRefinement, (function (b, selfSchema, inputVar, path) { + p: makeParseOperationBuilder(itemDefinitions, itemDefinitionsSet$1, definition, undefined, (function (b, input, selfSchema, path) { + var inputVar = $$var(b, input); var withUnknownKeysRefinement = selfSchema.t.unknownKeys === "Strict"; if (!withUnknownKeysRefinement) { return ; } if (itemDefinitions.length !== 0) { - var keyVar = $$var(b); + var key = allocateVal(b); + var keyVar = $$var(b, key); b.c = b.c + ("for(" + keyVar + " in " + inputVar + "){if("); for(var idx = 0 ,idx_finish = itemDefinitions.length; idx < idx_finish; ++idx){ var itemDefinition = itemDefinitions[idx]; @@ -1622,7 +1685,8 @@ function factory$3(definer) { }), keyVar) + "}}"); return ; } - var keyVar$1 = $$var(b); + var key$1 = allocateVal(b); + var keyVar$1 = $$var(b, key$1); b.c = b.c + ("for(" + keyVar$1 + " in " + inputVar + "){" + raiseWithArg(b, path, (function (exccessFieldName) { return { TAG: "ExcessField", @@ -1630,8 +1694,8 @@ function factory$3(definer) { }; }), keyVar$1) + "}"); })), - s: (function (b, param, path) { - var inputVar = toVar(b, b.i); + s: (function (b, input, param, path) { + var inputVar = $$var(b, input); var fieldsCodeRef = { contents: "" }; @@ -1663,7 +1727,7 @@ function factory$3(definer) { return invalidOperation(b, path, "The field " + definition.l + " is registered multiple times. If you want to duplicate the field, use S.transform instead"); } else { registeredDefinitions.add(definition); - fieldsCodeRef.contents = fieldsCodeRef.contents + (definition.l + ":" + use(b, definition.s, inputVar + outputPath, path + outputPath) + ","); + fieldsCodeRef.contents = fieldsCodeRef.contents + (definition.l + ":" + inline(b, use(b, definition.s, val(b, inputVar + outputPath), path + outputPath)) + ","); return ; } @@ -1684,7 +1748,7 @@ function factory$3(definer) { } } - return "{" + fieldsCodeRef.contents + "}"; + return val(b, "{" + fieldsCodeRef.contents + "}"); }), f: typeFilter, i: 0, @@ -1736,15 +1800,14 @@ function strict(schema) { } } -function builder(b, selfSchema, path) { - var input = b.i; +function builder(b, input, selfSchema, path) { b.c = b.c + raiseWithArg(b, path, (function (input) { return { TAG: "InvalidType", expected: selfSchema, received: input }; - }), input) + ";"; + }), inline(b, input)) + ";"; return input; } @@ -1809,8 +1872,8 @@ function min(schema, length, maybeMessage) { length: length }, message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + ".length<" + ("e[" + (b.e.push(length) - 1) + "]") + "){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + ".length<" + ("e[" + (b.e.push(length) - 1) + "]") + "){" + fail(b, message, path) + "}"; })); } @@ -1822,8 +1885,8 @@ function max(schema, length, maybeMessage) { length: length }, message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + ".length>" + ("e[" + (b.e.push(length) - 1) + "]") + "){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + ".length>" + ("e[" + (b.e.push(length) - 1) + "]") + "){" + fail(b, message, path) + "}"; })); } @@ -1835,8 +1898,8 @@ function length(schema, length$1, maybeMessage) { length: length$1 }, message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + ".length!==" + ("e[" + (b.e.push(length$1) - 1) + "]") + "){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + ".length!==" + ("e[" + (b.e.push(length$1) - 1) + "]") + "){" + fail(b, message, path) + "}"; })); } @@ -1845,8 +1908,8 @@ function email(schema, messageOpt) { return addRefinement(schema, metadataId, { kind: "Email", message: message - }, (function (b, inputVar, param, path) { - return "if(!" + ("e[" + (b.e.push(emailRegex) - 1) + "]") + ".test(" + inputVar + ")){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(!" + ("e[" + (b.e.push(emailRegex) - 1) + "]") + ".test(" + $$var(b, input) + ")){" + fail(b, message, path) + "}"; })); } @@ -1855,8 +1918,8 @@ function uuid(schema, messageOpt) { return addRefinement(schema, metadataId, { kind: "Uuid", message: message - }, (function (b, inputVar, param, path) { - return "if(!" + ("e[" + (b.e.push(uuidRegex) - 1) + "]") + ".test(" + inputVar + ")){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(!" + ("e[" + (b.e.push(uuidRegex) - 1) + "]") + ".test(" + $$var(b, input) + ")){" + fail(b, message, path) + "}"; })); } @@ -1865,8 +1928,8 @@ function cuid(schema, messageOpt) { return addRefinement(schema, metadataId, { kind: "Cuid", message: message - }, (function (b, inputVar, param, path) { - return "if(!" + ("e[" + (b.e.push(cuidRegex) - 1) + "]") + ".test(" + inputVar + ")){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(!" + ("e[" + (b.e.push(cuidRegex) - 1) + "]") + ".test(" + $$var(b, input) + ")){" + fail(b, message, path) + "}"; })); } @@ -1875,8 +1938,8 @@ function url(schema, messageOpt) { return addRefinement(schema, metadataId, { kind: "Url", message: message - }, (function (b, inputVar, param, path) { - return "try{new URL(" + inputVar + ")}catch(_){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "try{new URL(" + $$var(b, input) + ")}catch(_){" + fail(b, message, path) + "}"; })); } @@ -1888,9 +1951,10 @@ function pattern(schema, re, messageOpt) { re: re }, message: message - }, (function (b, inputVar, param, path) { - var reVar = $$var(b); - return reVar + "=" + ("e[" + (b.e.push(re) - 1) + "]") + ";" + reVar + ".lastIndex=0;if(!" + reVar + ".test(" + inputVar + ")){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + var reVal = val(b, "e[" + (b.e.push(re) - 1) + "]"); + var reVar = $$var(b, reVal); + return reVar + ".lastIndex=0;if(!" + reVar + ".test(" + $$var(b, input) + ")){" + fail(b, message, path) + "}"; })); } @@ -1901,7 +1965,7 @@ function datetime(schema, messageOpt) { message: message }; var refinements = schema.m[metadataId]; - return transform$1(set$1(schema, metadataId, refinements !== undefined ? refinements.concat(refinement) : [refinement]), (function (s) { + return transform$1(set$2(schema, metadataId, refinements !== undefined ? refinements.concat(refinement) : [refinement]), (function (s) { return { p: (function (string) { if (!datetimeRe.test(string)) { @@ -1942,22 +2006,23 @@ function factory$4(schema, spaceOpt) { return { t: "String", n: primitiveName, - p: (function (b, param, path) { - var input = b.i; - var jsonVar = $$var(b); - b.c = b.c + ("try{" + jsonVar + "=JSON.parse(" + input + ")}catch(t){" + raiseWithArg(b, path, (function (message) { + p: (function (b, input, param, path) { + var jsonVal = allocateVal(b); + b.c = b.c + ("try{" + set(b, jsonVal, map(b, "JSON.parse", input)) + "}catch(t){" + raiseWithArg(b, path, (function (message) { return { TAG: "OperationFailed", _0: message }; }), "t.message") + "}"); - return useWithTypeFilter(b, schema, jsonVar, path); + var b$1 = scope(b); + var val = useWithTypeFilter(b$1, schema, jsonVal, path); + exitScope(b$1); + return val; }), - s: (function (b, param, path) { - var input = b.i; - return "JSON.stringify(" + use(b, schema, input, path) + ( - space > 0 ? ",null," + space : "" - ) + ")"; + s: (function (b, input, param, path) { + return val(b, "JSON.stringify(" + inline(b, use(b, schema, input, path)) + ( + space > 0 ? ",null," + space : "" + ) + ")"); }), f: typeFilter$1, i: 0, @@ -2012,8 +2077,8 @@ function min$1(schema, minValue, maybeMessage) { value: minValue }, message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + "<" + ("e[" + (b.e.push(minValue) - 1) + "]") + "){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + "<" + ("e[" + (b.e.push(minValue) - 1) + "]") + "){" + fail(b, message, path) + "}"; })); } @@ -2025,8 +2090,8 @@ function max$1(schema, maxValue, maybeMessage) { value: maxValue }, message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + ">" + ("e[" + (b.e.push(maxValue) - 1) + "]") + "){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + ">" + ("e[" + (b.e.push(maxValue) - 1) + "]") + "){" + fail(b, message, path) + "}"; })); } @@ -2035,8 +2100,8 @@ function port(schema, messageOpt) { return addRefinement(schema, metadataId$1, { kind: "Port", message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + "<1||" + inputVar + ">65535){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + "<1||" + $$var(b, input) + ">65535){" + fail(b, message, path) + "}"; })); } @@ -2073,8 +2138,8 @@ function min$2(schema, minValue, maybeMessage) { value: minValue }, message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + "<" + ("e[" + (b.e.push(minValue) - 1) + "]") + "){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + "<" + ("e[" + (b.e.push(minValue) - 1) + "]") + "){" + fail(b, message, path) + "}"; })); } @@ -2086,8 +2151,8 @@ function max$2(schema, maxValue, maybeMessage) { value: maxValue }, message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + ">" + ("e[" + (b.e.push(maxValue) - 1) + "]") + "){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + ">" + ("e[" + (b.e.push(maxValue) - 1) + "]") + "){" + fail(b, message, path) + "}"; })); } @@ -2113,38 +2178,36 @@ function factory$5(schema) { _0: schema }, n: containerName, - p: (function (b, param, path) { - var inputVar = toVar(b, b.i); + p: (function (b, input, param, path) { + var inputVar = $$var(b, input); var iteratorVar = varWithoutAllocation(b); - var outputVar = $$var(b); - b.c = b.c + (outputVar + "=[];for(let " + iteratorVar + "=0;" + iteratorVar + "<" + inputVar + ".length;++" + iteratorVar + "){" + scope(b, (function (b) { - var itemOutputVar = withPathPrepend(b, path, iteratorVar, (function (b, path) { - return useWithTypeFilter(b, schema, inputVar + "[" + iteratorVar + "]", path); - })); - return outputVar + ".push(" + itemOutputVar + ")"; - })) + "}"); - var isAsync = schema.i; - if (!isAsync) { - return outputVar; + var output = val(b, "[]"); + var bb = scope(b); + var itemOutput = withPathPrepend(bb, path, iteratorVar, (function (b, path) { + return useWithTypeFilter(b, schema, val(b, inputVar + "[" + iteratorVar + "]"), path); + })); + var itemCode = allocateScope(bb); + b.c = b.c + ("for(let " + iteratorVar + "=0;" + iteratorVar + "<" + inputVar + ".length;++" + iteratorVar + "){" + itemCode + push(b, output, itemOutput) + "}"); + if (itemOutput.a) { + return asyncVal(b, "()=>Promise.all(" + $$var(b, output) + ".map(t=>t()))"); + } else { + return output; } - var asyncOutputVar = $$var(b); - b.c = b.c + (asyncOutputVar + "=()=>Promise.all(" + outputVar + ".map(t=>t()));"); - return asyncOutputVar; }), - s: (function (b, param, path) { + s: (function (b, input, param, path) { if (schema.s === noop) { - return b.i; + return input; } - var inputVar = toVar(b, b.i); + var inputVar = $$var(b, input); var iteratorVar = varWithoutAllocation(b); - var outputVar = $$var(b); - b.c = b.c + (outputVar + "=[];for(let " + iteratorVar + "=0;" + iteratorVar + "<" + inputVar + ".length;++" + iteratorVar + "){" + scope(b, (function (b) { - var itemOutputVar = withPathPrepend(b, path, iteratorVar, (function (b, path) { - return use(b, schema, inputVar + "[" + iteratorVar + "]", path); - })); - return outputVar + ".push(" + itemOutputVar + ")"; - })) + "}"); - return outputVar; + var output = val(b, "[]"); + var bb = scope(b); + var itemOutput = withPathPrepend(bb, path, iteratorVar, (function (b, path) { + return use(b, schema, val(b, inputVar + "[" + iteratorVar + "]"), path); + })); + var itemCode = allocateScope(bb); + b.c = b.c + ("for(let " + iteratorVar + "=0;" + iteratorVar + "<" + inputVar + ".length;++" + iteratorVar + "){" + itemCode + push(b, output, itemOutput) + "}"); + return output; }), f: typeFilter$5, i: 0, @@ -2160,8 +2223,8 @@ function min$3(schema, length, maybeMessage) { length: length }, message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + ".length<" + ("e[" + (b.e.push(length) - 1) + "]") + "){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + ".length<" + ("e[" + (b.e.push(length) - 1) + "]") + "){" + fail(b, message, path) + "}"; })); } @@ -2173,8 +2236,8 @@ function max$3(schema, length, maybeMessage) { length: length }, message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + ".length>" + ("e[" + (b.e.push(length) - 1) + "]") + "){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + ".length>" + ("e[" + (b.e.push(length) - 1) + "]") + "){" + fail(b, message, path) + "}"; })); } @@ -2186,8 +2249,8 @@ function length$1(schema, length$2, maybeMessage) { length: length$2 }, message: message - }, (function (b, inputVar, param, path) { - return "if(" + inputVar + ".length!==" + ("e[" + (b.e.push(length$2) - 1) + "]") + "){" + fail(b, message, path) + "}"; + }, (function (b, input, param, path) { + return "if(" + $$var(b, input) + ".length!==" + ("e[" + (b.e.push(length$2) - 1) + "]") + "){" + fail(b, message, path) + "}"; })); } @@ -2198,42 +2261,44 @@ function factory$6(schema) { _0: schema }, n: containerName, - p: (function (b, param, path) { - var inputVar = toVar(b, b.i); + p: (function (b, input, param, path) { + var inputVar = $$var(b, input); var keyVar = varWithoutAllocation(b); - var outputVar = $$var(b); - b.c = b.c + (outputVar + "={};for(let " + keyVar + " in " + inputVar + "){" + scope(b, (function (b) { - var itemOutputVar = withPathPrepend(b, path, keyVar, (function (b, path) { - return useWithTypeFilter(b, schema, inputVar + "[" + keyVar + "]", path); - })); - return outputVar + "[" + keyVar + "]=" + itemOutputVar; - })) + "}"); - var isAsync = schema.i; - if (!isAsync) { - return outputVar; + var bb = scope(b); + var itemInput = val(bb, inputVar + "[" + keyVar + "]"); + var itemOutput = withPathPrepend(bb, path, keyVar, (function (b, path) { + return useWithTypeFilter(b, schema, itemInput, path); + })); + var itemCode = allocateScope(bb); + var isTransformed = itemInput !== itemOutput; + var output = isTransformed ? val(b, "{}") : input; + b.c = b.c + ("for(let " + keyVar + " in " + inputVar + "){" + itemCode + ( + isTransformed ? addKey(b, output, keyVar, itemOutput) : "" + ) + "}"); + if (!itemOutput.a) { + return output; } var resolveVar = varWithoutAllocation(b); var rejectVar = varWithoutAllocation(b); var asyncParseResultVar = varWithoutAllocation(b); var counterVar = varWithoutAllocation(b); - var asyncOutputVar = $$var(b); - b.c = b.c + (asyncOutputVar + "=()=>new Promise((" + resolveVar + "," + rejectVar + ")=>{let " + counterVar + "=Object.keys(" + outputVar + ").length;for(let " + keyVar + " in " + outputVar + "){" + outputVar + "[" + keyVar + "]().then(" + asyncParseResultVar + "=>{" + outputVar + "[" + keyVar + "]=" + asyncParseResultVar + ";if(" + counterVar + "--===1){" + resolveVar + "(" + outputVar + ")}}," + rejectVar + ")}});"); - return asyncOutputVar; + var outputVar = $$var(b, output); + return asyncVal(b, "()=>new Promise((" + resolveVar + "," + rejectVar + ")=>{let " + counterVar + "=Object.keys(" + outputVar + ").length;for(let " + keyVar + " in " + outputVar + "){" + outputVar + "[" + keyVar + "]().then(" + asyncParseResultVar + "=>{" + outputVar + "[" + keyVar + "]=" + asyncParseResultVar + ";if(" + counterVar + "--===1){" + resolveVar + "(" + outputVar + ")}}," + rejectVar + ")}})"); }), - s: (function (b, param, path) { + s: (function (b, input, param, path) { if (schema.s === noop) { - return b.i; + return input; } - var inputVar = toVar(b, b.i); + var inputVar = $$var(b, input); + var output = val(b, "{}"); var keyVar = varWithoutAllocation(b); - var outputVar = $$var(b); - b.c = b.c + (outputVar + "={};for(let " + keyVar + " in " + inputVar + "){" + scope(b, (function (b) { - var itemOutputVar = withPathPrepend(b, path, keyVar, (function (b, path) { - return use(b, schema, inputVar + "[" + keyVar + "]", path); - })); - return outputVar + "[" + keyVar + "]=" + itemOutputVar; - })) + "}"); - return outputVar; + var bb = scope(b); + var itemOutput = withPathPrepend(bb, path, keyVar, (function (b, path) { + return use(b, schema, val(b, inputVar + "[" + keyVar + "]"), path); + })); + var itemCode = allocateScope(bb); + b.c = b.c + ("for(let " + keyVar + " in " + inputVar + "){" + itemCode + addKey(b, output, keyVar, itemOutput) + "}"); + return output; }), f: typeFilter, i: 0, @@ -2299,7 +2364,8 @@ function factory$7(definer) { return s.n(); }).join(", ") + ")"; }), - p: makeParseOperationBuilder(itemDefinitions, itemDefinitionsSet$1, definition, (function (b, param, inputVar, path) { + p: makeParseOperationBuilder(itemDefinitions, itemDefinitionsSet$1, definition, (function (b, input, param, path) { + var inputVar = $$var(b, input); b.c = b.c + ("if(" + inputVar + ".length!==" + length + "){" + raiseWithArg(b, path, (function (numberOfInputItems) { return { TAG: "InvalidTupleSize", @@ -2308,11 +2374,9 @@ function factory$7(definer) { }; }), inputVar + ".length") + "}"); }), noopRefinement), - s: (function (b, param, path) { - var inputVar = toVar(b, b.i); - var outputVar = $$var(b); + s: (function (b, input, param, path) { + var output = val(b, "[]"); var registeredDefinitions = new Set(); - b.c = b.c + (outputVar + "=[];"); var prevCode = b.c; b.c = ""; var definitionToOutput = function (definition, outputPath) { @@ -2327,21 +2391,21 @@ function factory$7(definer) { } return ; case 1 : - b.c = "if(" + inputVar + outputPath + "!==" + ("e[" + (b.e.push(definition) - 1) + "]") + "){" + raiseWithArg(b, path + outputPath, (function (input) { + b.c = "if(" + $$var(b, input) + outputPath + "!==" + ("e[" + (b.e.push(definition) - 1) + "]") + "){" + raiseWithArg(b, path + outputPath, (function (input) { return { TAG: "InvalidLiteral", expected: parseInternal(definition), received: input }; - }), inputVar + outputPath) + "}" + b.c; + }), $$var(b, input) + outputPath) + "}" + b.c; return ; case 2 : if (registeredDefinitions.has(definition)) { return invalidOperation(b, path, "The item " + definition.l + " is registered multiple times. If you want to duplicate the item, use S.transform instead"); } registeredDefinitions.add(definition); - var fieldOuputVar = use(b, definition.s, inputVar + outputPath, path + outputPath); - b.c = b.c + (outputVar + definition.p + "=" + fieldOuputVar + ";"); + var fieldOuputVar = inline(b, use(b, definition.s, val(b, $$var(b, input) + outputPath), path + outputPath)); + b.c = b.c + ($$var(b, output) + definition.p + "=" + fieldOuputVar + ";"); return ; } @@ -2353,14 +2417,14 @@ function factory$7(definer) { if (!registeredDefinitions.has(itemDefinition)) { var literal = toInternalLiteral(itemDefinition.s); if (literal !== undefined) { - b.c = b.c + (outputVar + itemDefinition.p + "=" + ("e[" + (b.e.push(literal.value) - 1) + "]") + ";"); + b.c = b.c + ($$var(b, output) + itemDefinition.p + "=" + ("e[" + (b.e.push(literal.value) - 1) + "]") + ";"); } else { invalidOperation(b, path, "Can't create serializer. The " + itemDefinition.l + " item is not registered and not a literal. Use S.transform instead"); } } } - return outputVar; + return output; }), f: typeFilter$5, i: 0, @@ -2384,104 +2448,89 @@ function factory$8(schemas) { return s.n(); }).join(", ") + ")"; }), - p: (function (b, selfSchema, path) { - var inputVar = toVar(b, b.i); + p: (function (b, input, selfSchema, path) { var schemas = selfSchema.t._0; - var isAsyncRef = false; - var itemsCode = []; - var itemsOutputVar = []; - var prevCode = b.c; - for(var idx = 0 ,idx_finish = schemas.length; idx < idx_finish; ++idx){ - var schema = schemas[idx]; - b.c = ""; - var itemOutputVar = withBuildErrorInline(b, (function () { - return useWithTypeFilter(b, schema, inputVar, ""); - })); - var isAsyncItem = schema.i; - if (isAsyncItem) { - isAsyncRef = true; - } - itemsOutputVar.push(itemOutputVar); - itemsCode.push(b.c); - } - b.c = prevCode; - var isAsync = isAsyncRef; - var outputVar = $$var(b); + var output = allocateVal(b); var codeEndRef = ""; var errorCodeRef = ""; - for(var idx$1 = 0 ,idx_finish$1 = schemas.length; idx$1 < idx_finish$1; ++idx$1){ - var schema$1 = schemas[idx$1]; - var code = itemsCode[idx$1]; - var itemOutputVar$1 = itemsOutputVar[idx$1]; - var isAsyncItem$1 = schema$1.i; - var errorVar = varWithoutAllocation(b); - var errorCode = isAsync ? ( - isAsyncItem$1 ? errorVar + "===" + itemOutputVar$1 + "?" + errorVar + "():" : "" - ) + ("Promise.reject(" + errorVar + ")") : errorVar; - errorCodeRef = idx$1 === 0 ? errorCode : errorCodeRef + "," + errorCode; - b.c = b.c + ("try{" + code + ( - isAsyncItem$1 ? "throw " + itemOutputVar$1 : ( - isAsync ? outputVar + "=()=>Promise.resolve(" + itemOutputVar$1 + ")" : outputVar + "=" + itemOutputVar$1 - ) - ) + "}catch(" + errorVar + "){if(" + (errorVar + "&&" + errorVar + ".s===s") + ( - isAsyncItem$1 ? "||" + errorVar + "===" + itemOutputVar$1 : "" - ) + "){"); - codeEndRef = "}else{throw " + errorVar + "}}" + codeEndRef; + var isAsync = false; + for(var idx = 0 ,idx_finish = schemas.length; idx < idx_finish; ++idx){ + var prevCode = b.c; + try { + var schema = schemas[idx]; + var errorVar = "e" + idx; + b.c = b.c + "try{"; + var itemOutput = useWithTypeFilter(b, schema, input, ""); + if (itemOutput.a) { + isAsync = true; + } + b.c = b.c + (set(b, output, itemOutput) + "}catch(" + errorVar + "){"); + codeEndRef = codeEndRef + "}"; + errorCodeRef = errorCodeRef + errorVar + ","; + } + catch (raw_exn){ + var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); + var value = getOrRethrow(exn); + errorCodeRef = errorCodeRef + ("e[" + (b.e.push(value) - 1) + "]") + ","; + b.c = prevCode; + } } if (isAsync) { - b.c = b.c + (outputVar + "=()=>Promise.any([" + errorCodeRef + "]).catch(t=>{" + raiseWithArg(b, path, (function (internalErrors) { - return { - TAG: "InvalidUnion", - _0: internalErrors - }; - }), "t.errors") + "})") + codeEndRef; - return outputVar; + invalidOperation(b, path, "S.union doesn't support async items. Please create an issue to rescript-schema if you nead the feature."); + } + b.c = b.c + raiseWithArg(b, path, (function (internalErrors) { + return { + TAG: "InvalidUnion", + _0: internalErrors + }; + }), "[" + errorCodeRef + "]") + codeEndRef; + var isAllSchemasBuilderFailed = codeEndRef === ""; + if (isAllSchemasBuilderFailed) { + b.c = b.c + ";"; + return input; } else { - b.c = b.c + raiseWithArg(b, path, (function (internalErrors) { - return { - TAG: "InvalidUnion", - _0: internalErrors - }; - }), "[" + errorCodeRef + "]") + codeEndRef; - return outputVar; + return output; } }), - s: (function (b, selfSchema, path) { - var inputVar = toVar(b, b.i); + s: (function (b, input, selfSchema, path) { var schemas = selfSchema.t._0; - var outputVar = $$var(b); + var output = allocateVal(b); var codeEndRef = ""; - var errorVarsRef = ""; + var errorCodeRef = ""; for(var idx = 0 ,idx_finish = schemas.length; idx < idx_finish; ++idx){ - var itemSchema = schemas[idx]; - var errorVar = varWithoutAllocation(b); - errorVarsRef = errorVarsRef + errorVar + ","; - b.c = b.c + ("try{" + scope(b, (function(itemSchema){ - return function (b) { - var itemOutput = withBuildErrorInline(b, (function () { - return use(b, itemSchema, inputVar, ""); - })); - var typeFilter = itemSchema.f; - var itemOutput$1; - if (typeFilter !== undefined) { - var itemOutputVar = toVar(b, itemOutput); - b.c = b.c + typeFilterCode(b, typeFilter, itemSchema, itemOutputVar, ""); - itemOutput$1 = itemOutputVar; - } else { - itemOutput$1 = itemOutput; - } - return outputVar + "=" + itemOutput$1; - } - }(itemSchema))) + "}catch(" + errorVar + "){if(" + (errorVar + "&&" + errorVar + ".s===s") + "){"); - codeEndRef = "}else{throw " + errorVar + "}}" + codeEndRef; + var prevCode = b.c; + try { + var schema = schemas[idx]; + var errorVar = "e" + idx; + b.c = b.c + "try{"; + var itemOutput = use(b, schema, input, ""); + var typeFilter = schema.f; + b.c = b.c + ( + typeFilter !== undefined ? typeFilterCode(b, typeFilter, schema, itemOutput, "") : "" + ) + (set(b, output, itemOutput) + "}catch(" + errorVar + "){"); + codeEndRef = codeEndRef + "}"; + errorCodeRef = errorCodeRef + errorVar + ","; + } + catch (raw_exn){ + var exn = Caml_js_exceptions.internalToOCamlException(raw_exn); + var value = getOrRethrow(exn); + errorCodeRef = errorCodeRef + ("e[" + (b.e.push(value) - 1) + "]") + ","; + b.c = prevCode; + } } b.c = b.c + raiseWithArg(b, path, (function (internalErrors) { return { TAG: "InvalidUnion", _0: internalErrors }; - }), "[" + errorVarsRef + "]") + codeEndRef; - return outputVar; + }), "[" + errorCodeRef + "]") + codeEndRef; + var isAllSchemasBuilderFailed = codeEndRef === ""; + if (isAllSchemasBuilderFailed) { + b.c = b.c + ";"; + return input; + } else { + return output; + } }), f: undefined, i: 0, @@ -2507,7 +2556,7 @@ function json(validate) { validated: validate }, n: primitiveName, - p: validate ? (function (b, selfSchema, path) { + p: validate ? (function (b, input, selfSchema, path) { var parse = function (input, pathOpt) { var path$1 = pathOpt !== undefined ? pathOpt : path; var match = typeof input; @@ -2552,8 +2601,7 @@ function json(validate) { received: input }, "Parsing", path$1); }; - var input = b.i; - return "e[" + (b.e.push(parse) - 1) + "](" + input + ")"; + return map(b, "e[" + (b.e.push(parse) - 1) + "]", input); }) : noop, s: noop, f: undefined, @@ -2568,25 +2616,25 @@ function $$catch(schema, getFallbackValue) { return { t: schema.t, n: schema.n, - p: (function (b, selfSchema, path) { - var inputVar = toVar(b, b.i); + p: (function (b, input, selfSchema, path) { + var inputVar = $$var(b, input); return withCatch(b, (function (b, errorVar) { - return "e[" + (b.e.push(function (input, internalError) { - return getFallbackValue({ - e: internalError, - i: input, - s: selfSchema, - f: (function (message, customPathOpt) { - var customPath = customPathOpt !== undefined ? customPathOpt : ""; - throw new RescriptSchemaError({ - TAG: "OperationFailed", - _0: message - }, b.o, path + customPath); - }) - }); - }) - 1) + "](" + inputVar + "," + errorVar + ")"; + return val(b, "e[" + (b.e.push(function (input, internalError) { + return getFallbackValue({ + e: internalError, + i: input, + s: selfSchema, + f: (function (message, customPathOpt) { + var customPath = customPathOpt !== undefined ? customPathOpt : ""; + throw new RescriptSchemaError({ + TAG: "OperationFailed", + _0: message + }, b.o, path + customPath); + }) + }); + }) - 1) + "](" + inputVar + "," + errorVar + ")"); }), (function (b) { - return useWithTypeFilter(b, schema, inputVar, path); + return useWithTypeFilter(b, schema, input, path); })); }), s: schema.s, @@ -2599,7 +2647,7 @@ function $$catch(schema, getFallbackValue) { var deprecationMetadataId = "rescript-schema:deprecation"; function deprecate(schema, message) { - return set$1(schema, deprecationMetadataId, message); + return set$2(schema, deprecationMetadataId, message); } function deprecation(schema) { @@ -2609,7 +2657,7 @@ function deprecation(schema) { var descriptionMetadataId = "rescript-schema:description"; function describe(schema, description) { - return set$1(schema, descriptionMetadataId, description); + return set$2(schema, descriptionMetadataId, description); } function description(schema) { @@ -2973,7 +3021,7 @@ function internalInline(schema, maybeVariant, param) { } } -function inline(schema) { +function inline$1(schema) { if (false) { var v = (void 0); if (v !== undefined) { @@ -3197,13 +3245,12 @@ function js_merge(s1, s2) { n: (function () { return s1.n() + " & " + s2.n(); }), - p: (function (b, param, path) { - var inputVar = toVar(b, b.i); - var s1Result = use(b, s1, inputVar, path); - var s2Result = use(b, s2, inputVar, path); - return "Object.assign(" + s1Result + ", " + s2Result + ")"; + p: (function (b, input, param, path) { + var s1Result = use(b, s1, input, path); + var s2Result = use(b, s2, input, path); + return val(b, "Object.assign(" + inline(b, s1Result) + ", " + inline(b, s2Result) + ")"); }), - s: (function (b, param, path) { + s: (function (b, param, param$1, path) { return invalidOperation(b, path, "The S.merge serializing is not supported yet"); }), f: typeFilter, @@ -3256,9 +3303,9 @@ var $$int = schema$4; var $$float = schema$5; -var array = factory$5; +var array$1 = factory$5; -var dict = factory$6; +var dict$1 = factory$6; var option = factory$1; @@ -3349,7 +3396,7 @@ var $$Array = { var Metadata = { Id: Id, get: get, - set: set$1 + set: set$2 }; export { @@ -3366,9 +3413,9 @@ export { $$float , json , literal , - array , + array$1 as array, list , - dict , + dict$1 as dict, option , $$null$1 as $$null, nullable , @@ -3419,7 +3466,7 @@ export { Float , $$Array , Metadata , - inline , + inline$1 as inline, js_optional , js_tuple , js_custom , diff --git a/src/S_Core.res b/src/S_Core.res index ec8c1575..9235dd4c 100644 --- a/src/S_Core.res +++ b/src/S_Core.res @@ -267,21 +267,29 @@ and tagged = | Dict(t) | JSON({validated: bool}) and builder -and builderCtx = { +and val = { + @as("v") + mutable _var?: string, + @as("i") + _initial?: string, + @as("s") + _scope: b, @as("a") - mutable isAsyncBranch: bool, + mutable isAsync: bool, +} +and b = { @as("c") mutable code: string, @as("o") operation: operation, @as("v") mutable _varCounter: int, - @as("s") - mutable _vars: Stdlib.Set.t, @as("l") mutable _varsAllocation: string, - @as("i") - mutable _input: string, + @as("a") + mutable _isAllocated: bool, + @as("p") + mutable _parent: b, @as("e") _embeded: array, } @@ -384,120 +392,184 @@ let classify = schema => schema.tagged module Builder = { type t = builder - type ctx = builderCtx - type implementation = (ctx, ~selfSchema: schema, ~path: Path.t) => string - + type implementation = (b, ~input: val, ~selfSchema: schema, ~path: Path.t) => val let make = (Obj.magic: implementation => t) - module Ctx = { - type t = ctx - + module B = { @inline - let embed = (b: t, value) => { + let embed = (b: b, value) => { `e[${(b._embeded->Js.Array2.push(value->castAnyToUnknown)->(Obj.magic: int => float) -. 1.) ->(Obj.magic: float => string)}]` } - let scope = (b: t, fn) => { - let prevVarsAllocation = b._varsAllocation - let prevCode = b.code - b._varsAllocation = "" - b.code = "" - let resultCode = fn(b) + let scope = (b: b): b => { + { + _parent: b, + _embeded: b._embeded, + _varCounter: b._varCounter, + code: "", + operation: b.operation, + _varsAllocation: "", + _isAllocated: false, + } + } + + let allocateScope = (b: b): string => { let varsAllocation = b._varsAllocation - let code = varsAllocation === "" ? b.code : `let ${varsAllocation};${b.code}` - b._varsAllocation = prevVarsAllocation - b.code = prevCode - code ++ resultCode + b._isAllocated = true + b._parent._varCounter = b._varCounter + varsAllocation === "" ? b.code : `let ${varsAllocation};${b.code}` } - let varWithoutAllocation = (b: t) => { + let exitScope = (b: b): b => { + let parent = b._parent + parent.code = parent.code ++ b->allocateScope + parent + } + + let varWithoutAllocation = (b: b) => { let newCounter = b._varCounter->Stdlib.Int.plus(1) b._varCounter = newCounter - let v = `v${newCounter->Stdlib.Int.unsafeToString}` - b._vars->Stdlib.Set.add(v)->ignore - v + `v${newCounter->Stdlib.Int.unsafeToString}` } - let var = (b: t) => { - let v = b->varWithoutAllocation - b._varsAllocation = b._varsAllocation === "" ? v : b._varsAllocation ++ "," ++ v - v + let allocateVal = (b: b): val => { + {_scope: b, isAsync: false} } - @inline - let useInput = b => { - b._input + let val = (b: b, initial: string): val => { + {_initial: initial, _scope: b, isAsync: false} } - let toVar = (b, val) => - if b._vars->Stdlib.Set.has(val) { - val - } else { - let var = b->var - b.code = b.code ++ `${var}=${val};` - var + let asyncVal = (b: b, initial: string): val => { + {_initial: initial, _scope: b, isAsync: true} + } + + module Val = { + let inline = (_b: b, val: val) => { + switch val { + | {_var: var} => var + | _ => val._initial->(Obj.magic: option => string) // There should never be the case when we inline not allocated val + } } - @inline - let useInputVar = b => { - b->toVar(b->useInput) + let var = (b: b, val: val) => { + switch val { + | {_var} => _var + | _ => { + let var = b->varWithoutAllocation + let isValScopeActive = !val._scope._isAllocated + let activeScope = isValScopeActive ? val._scope : b + let allocation = switch val._initial { + | Some(i) if isValScopeActive => `${var}=${i}` + | _ => var + } + let varsAllocation = activeScope._varsAllocation + activeScope._varsAllocation = varsAllocation === "" + ? allocation + : varsAllocation ++ "," ++ allocation + switch val._initial { + | Some(i) if !isValScopeActive => b.code = b.code ++ `${var}=${i};` + | _ => () + } + val._var = Some(var) + var + } + } + } + + let push = (b: b, input: val, val: val) => { + `${b->var(input)}.push(${b->inline(val)})` + } + + let addKey = (b: b, input: val, key, val: val) => { + `${b->var(input)}[${key}]=${b->inline(val)}` + } + + let set = (b: b, input: val, val) => { + if input === val { + "" + } else { + switch (input, val) { + | ({isAsync: false}, {isAsync: true}) => { + input.isAsync = true + `${b->var(input)}=${b->inline(val)}` + } + | ({isAsync: false}, {isAsync: false}) + | ({isAsync: true}, {isAsync: true}) => + `${b->var(input)}=${b->inline(val)}` + | ({isAsync: true}, {isAsync: false}) => + `${b->var(input)}=()=>Promise.resolve(${b->inline(val)})` + } + } + } + + let setInlined = (b: b, input: val, inlined) => { + `${b->var(input)}=${inlined}` + } + + let map = (b: b, inlinedFn, input: val) => { + b->val(`${inlinedFn}(${b->inline(input)})`) + } } @inline - let isInternalError = (_b: t, var) => { + let isInternalError = (_b: b, var) => { `${var}&&${var}.s===s` } - let transform = (b: t, ~input, ~isAsync, operation) => { - if b.isAsyncBranch === true { + let transform = (b: b, ~input, operation) => { + if input.isAsync { let prevCode = b.code b.code = "" - let inputVar = b->varWithoutAllocation - let operationOutputVar = operation(b, ~input=inputVar) - let outputVar = b->var - b.code = - prevCode ++ - `${outputVar}=()=>${input}().then(${inputVar}=>{${b.code}return ${operationOutputVar}${isAsync - ? "()" - : ""}});` - outputVar - } else if isAsync { - b.isAsyncBranch = true - // TODO: Would be nice to remove. Needed to enforce that async ops are always vars - let outputVar = b->var - b.code = b.code ++ `${outputVar}=${operation(b, ~input)};` - outputVar + let operationInput: val = { + _var: b->varWithoutAllocation, + _scope: b, + isAsync: false, + } + let operationOutputVal = operation(~input=operationInput) + let operationCode = b.code + b.code = prevCode + b->asyncVal( + // TODO: Use Val.inline + `()=>${b->Val.var(input)}().then(${b->Val.var( + operationInput, + )}=>{${operationCode}return ${operationOutputVal.isAsync + ? "(" ++ b->Val.inline(operationOutputVal) ++ ")()" + : b->Val.inline(operationOutputVal)}})`, + ) } else { - operation(b, ~input) + operation(~input) } } - let embedSyncOperation = (b: t, ~input, ~fn: 'input => 'output) => { - b->transform(~input, ~isAsync=false, (b, ~input) => { - `${b->embed(fn)}(${input})` + let embedSyncOperation = (b: b, ~input, ~fn: 'input => 'output) => { + b->transform(~input, (~input) => { + b->Val.map(b->embed(fn), input) }) } - let embedAsyncOperation = (b: t, ~input, ~fn: 'input => unit => promise<'output>) => { - b->transform(~input, ~isAsync=true, (b, ~input) => { - `${b->embed(fn)}(${input})` + let embedAsyncOperation = (b: b, ~input, ~fn: 'input => unit => promise<'output>) => { + b->transform(~input, (~input) => { + let val = b->Val.map(b->embed(fn), input) + val.isAsync = true + val }) } - let raiseWithArg = (b: t, ~path, fn: 'arg => errorCode, arg) => { + let raiseWithArg = (b: b, ~path, fn: 'arg => errorCode, arg) => { `${b->embed(arg => { InternalError.raise(~path, ~code=fn(arg), ~operation=b.operation) })}(${arg})` } - let fail = (b: t, ~message, ~path) => { + let fail = (b: b, ~message, ~path) => { `${b->embed(() => { InternalError.raise(~path, ~code=OperationFailed(message), ~operation=b.operation) })}()` } - let invalidOperation = (b: t, ~path, ~description) => { + let invalidOperation = (b: b, ~path, ~description) => { InternalError.raise( ~path, ~code=InvalidOperation({description: description}), @@ -505,52 +577,58 @@ module Builder = { ) } - let withCatch = (b: t, ~catch, fn) => { + // TODO: Refactor + let withCatch = (b: b, ~catch, fn) => { let prevCode = b.code b.code = "" let errorVar = b->varWithoutAllocation - let maybeResolveVar = catch(b, ~errorVar) + let maybeResolveVal = catch(b, ~errorVar) let catchCode = `if(${b->isInternalError(errorVar)}){${b.code}` - b.code = "" + + let b = b->scope let fnOutput = fn(b) - let isAsync = b.isAsyncBranch - let isInlined = !(b._vars->Stdlib.Set.has(fnOutput)) + let b = b->exitScope + let isAsync = fnOutput.isAsync - let outputVar = isAsync || isInlined ? b->var : fnOutput + let output = b->allocateVal + output.isAsync = isAsync - let catchCode = switch maybeResolveVar { + let catchCode = switch maybeResolveVal { | None => _ => `${catchCode}}throw ${errorVar}` - | Some(resolveVar) => + | Some(resolveVal) => catchLocation => catchCode ++ switch catchLocation { - | #0 if isAsync => `${outputVar}=()=>Promise.resolve(${resolveVar})` - | #0 => `${outputVar}=${resolveVar}` - | #1 => `return Promise.resolve(${resolveVar})` - | #2 => `return ${resolveVar}` + | #0 => b->Val.set(output, resolveVal) + | #1 => `return Promise.resolve(${b->Val.inline(resolveVal)})` + | #2 => `return ${b->Val.inline(resolveVal)}` } ++ `}else{throw ${errorVar}}` } + let fnOutputVar = b->Val.var(fnOutput) + b.code = prevCode ++ `try{${b.code}${{ - switch (isAsync, isInlined) { - | (true, _) => - `${outputVar}=()=>{try{return ${fnOutput}().catch(${errorVar}=>{${catchCode( - #2, - )}})}catch(${errorVar}){${catchCode(#1)}}};` - | (_, true) => `${outputVar}=${fnOutput}` - | _ => "" + switch isAsync { + | true => + b->Val.setInlined( + output, + `()=>{try{return ${fnOutputVar}().catch(${errorVar}=>{${catchCode( + #2, + )}})}catch(${errorVar}){${catchCode(#1)}}}`, + ) + | false => b->Val.set(output, fnOutput) } }}}catch(${errorVar}){${catchCode(#0)}}` - outputVar + output } - let withPathPrepend = (b: t, ~path, ~dynamicLocationVar as maybeDynamicLocationVar=?, fn) => { + let withPathPrepend = (b: b, ~path, ~dynamicLocationVar as maybeDynamicLocationVar=?, fn) => { if path === Path.empty && maybeDynamicLocationVar === None { fn(b, ~path) } else { @@ -574,7 +652,8 @@ module Builder = { } } - let typeFilterCode = (b: t, ~typeFilter, ~schema, ~inputVar, ~path) => { + let typeFilterCode = (b: b, ~typeFilter, ~schema, ~input, ~path) => { + let inputVar = b->Val.var(input) `if(${typeFilter(~inputVar)}){${b->raiseWithArg( ~path, input => InvalidType({ @@ -585,51 +664,30 @@ module Builder = { )}}` } - let use = (b: t, ~schema, ~input, ~path) => { - let isParentAsync = b.isAsyncBranch + let use = (b: b, ~schema, ~input, ~path) => { let isParsing = b.operation === Parsing - b._input = input - b.isAsyncBranch = false - let output = ( + + ( (isParsing ? schema.parseOperationBuilder : schema.serializeOperationBuilder)->( Obj.magic: builder => implementation ) - )(b, ~selfSchema=schema, ~path) - if isParsing { - schema.isAsyncParse = Value(b.isAsyncBranch) - b.isAsyncBranch = isParentAsync || b.isAsyncBranch - } - output - } - - let useWithTypeFilter = (b: t, ~schema, ~input, ~path) => { - let input = switch schema.maybeTypeFilter { - | Some(typeFilter) => { - let inputVar = b->toVar(input) - b.code = b.code ++ b->typeFilterCode(~schema, ~typeFilter, ~inputVar, ~path) - inputVar - } - | None => input - } - b->use(~schema, ~input, ~path) + )(b, ~input, ~selfSchema=schema, ~path) } - let withBuildErrorInline = (b: t, fn) => { - try { - fn() - } catch { - | exn => { - let error = exn->InternalError.getOrRethrow - b.code = `throw ${b->embed(error)};` - b._input - } + let useWithTypeFilter = (b: b, ~schema, ~input, ~path) => { + switch schema.maybeTypeFilter { + | Some(typeFilter) => + b.code = b.code ++ b->typeFilterCode(~schema, ~typeFilter, ~input, ~path) + | None => () } + let b = b->scope + let val = b->use(~schema, ~input, ~path) + let _ = b->exitScope + val } } - let noop = make((b, ~selfSchema as _, ~path as _) => { - b->Ctx.useInput - }) + let noop = make((_b, ~input, ~selfSchema as _, ~path as _) => input) let noopOperation = i => i->Obj.magic @@ -640,16 +698,17 @@ module Builder = { let b = { _embeded: [], _varCounter: -1, - _varsAllocation: "", - _vars: Stdlib.Set.fromArray([intitialInputVar]), - _input: intitialInputVar, code: "", - isAsyncBranch: false, + _varsAllocation: "", + _isAllocated: false, + _parent: %raw(`void 0`), operation, } + let input = {_var: intitialInputVar, _scope: b, isAsync: false} let output = (builder->(Obj.magic: builder => implementation))( b, + ~input, ~selfSchema=schema, ~path=Path.empty, ) @@ -661,22 +720,16 @@ module Builder = { if operation === Parsing { switch schema.maybeTypeFilter { | Some(typeFilter) => - b.code = - b->Ctx.typeFilterCode( - ~schema, - ~typeFilter, - ~inputVar=intitialInputVar, - ~path=Path.empty, - ) ++ b.code + b.code = b->B.typeFilterCode(~schema, ~typeFilter, ~input, ~path=Path.empty) ++ b.code | None => () } - schema.isAsyncParse = Value(b.isAsyncBranch) + schema.isAsyncParse = Value(output.isAsync) } - if b.code === "" && output === intitialInputVar { + if b.code === "" && output === input { noopOperation } else { - let inlinedFunction = `${intitialInputVar}=>{${b.code}return ${output}}` + let inlinedFunction = `${intitialInputVar}=>{${b.code}return ${b->B.Val.inline(output)}}` // Js.log(inlinedFunction) @@ -691,7 +744,7 @@ module Builder = { } } // TODO: Split validation code and transformation code -module B = Builder.Ctx +module B = Builder.B module Literal = { open Stdlib @@ -702,7 +755,7 @@ module Literal = { @as("s") string: string, @as("b") - checkBuilder: (B.t, ~inputVar: string, ~literal: literal) => string, + checkBuilder: (b, ~inputVar: string, ~literal: literal) => string, @as("j") isJsonable: bool, @as("i") @@ -788,7 +841,7 @@ module Literal = { value: %raw(`undefined`), string: "undefined", isJsonable: false, - checkBuilder: (_, ~inputVar, ~literal as _) => `${inputVar}===void 0`, + checkBuilder: inlinedStrictEqualCheckBuilder, } let null = { @@ -966,11 +1019,11 @@ let toInternalLiteral = { | Tuple(tupleSchemas) => tupleSchemas ->Js.Array2.map(itemSchema => (itemSchema->loop).value) - ->Literal.parseInternal + ->Literal.array | Object({fields}) => fields ->Stdlib.Dict.mapValues(itemSchema => (itemSchema->loop).value) - ->Literal.parseInternal + ->Literal.dict | String | Int | Float @@ -1096,7 +1149,7 @@ let makeWithNoopSerializer = ( } module Operation = { - let unexpectedAsync = _ => + let unexpectedAsync = () => InternalError.raise(~path=Path.empty, ~code=UnexpectedAsync, ~operation=Parsing) type label = @@ -1292,26 +1345,29 @@ let recursive = fn => { { let builder = placeholder.parseOperationBuilder - placeholder.parseOperationBuilder = Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput + placeholder.parseOperationBuilder = Builder.make((b, ~input, ~selfSchema, ~path) => { let isAsync = { selfSchema.parseOperationBuilder = Builder.noop - let ctx = { + let b = { _embeded: [], - _varsAllocation: "", code: "", - _input: Builder.intitialInputVar, _varCounter: -1, - _vars: Stdlib.Set.fromArray([Builder.intitialInputVar]), - isAsyncBranch: false, operation: Parsing, + _parent: %raw(`void 0`), + _varsAllocation: "", + _isAllocated: false, } - let _ = (builder->(Obj.magic: builder => Builder.implementation))(ctx, ~selfSchema, ~path) - ctx.isAsyncBranch + let input = {_var: Builder.intitialInputVar, _scope: b, isAsync: false} + let output = (builder->(Obj.magic: builder => Builder.implementation))( + b, + ~input, + ~selfSchema, + ~path, + ) + output.isAsync } - selfSchema.parseOperationBuilder = Builder.make((b, ~selfSchema, ~path as _) => { - let input = b->B.useInput + selfSchema.parseOperationBuilder = Builder.make((b, ~input, ~selfSchema, ~path as _) => { if isAsync { b->B.embedAsyncOperation(~input, ~fn=input => input->internalParseAsyncWith(selfSchema)) } else { @@ -1328,6 +1384,7 @@ let recursive = fn => { } selfSchema.parseOperationBuilder = builder + b->B.withPathPrepend(~path, (b, ~path as _) => if isAsync { b->B.embedAsyncOperation(~input, ~fn=operation) @@ -1340,10 +1397,8 @@ let recursive = fn => { { let builder = placeholder.serializeOperationBuilder - placeholder.serializeOperationBuilder = Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput - selfSchema.serializeOperationBuilder = Builder.make((b, ~selfSchema, ~path as _) => { - let input = b->B.useInput + placeholder.serializeOperationBuilder = Builder.make((b, ~input, ~selfSchema, ~path) => { + selfSchema.serializeOperationBuilder = Builder.make((b, ~input, ~selfSchema, ~path as _) => { b->B.embedSyncOperation( ~input, ~fn=input => input->serializeToUnknownOrRaiseWith(selfSchema), @@ -1389,22 +1444,19 @@ let internalRefine = (schema, refiner) => { make( ~name=schema.name, ~tagged=schema.tagged, - ~parseOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput - b->B.transform(~input=b->B.use(~schema, ~input, ~path), ~isAsync=false, (b, ~input) => { - let inputVar = b->B.toVar(input) - b.code = b.code ++ refiner(b, ~inputVar, ~selfSchema, ~path) - inputVar + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { + b->B.transform(~input=b->B.use(~schema, ~input, ~path), (~input) => { + let rCode = refiner(b, ~input, ~selfSchema, ~path) + b.code = b.code ++ rCode + input }) }), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { b->B.use( ~schema, - ~input=b->B.transform(~input, ~isAsync=false, (b, ~input) => { - let inputVar = b->B.toVar(input) - b.code = b.code ++ refiner(b, ~inputVar, ~selfSchema, ~path) - inputVar + ~input=b->B.transform(~input, (~input) => { + b.code = b.code ++ refiner(b, ~input, ~selfSchema, ~path) + input }), ~path, ) @@ -1415,10 +1467,10 @@ let internalRefine = (schema, refiner) => { } let refine: (t<'value>, s<'value> => 'value => unit) => t<'value> = (schema, refiner) => { - schema->internalRefine((b, ~inputVar, ~selfSchema, ~path) => { + schema->internalRefine((b, ~input, ~selfSchema, ~path) => { `${b->B.embed( refiner(EffectCtx.make(~selfSchema, ~path, ~operation=b.operation)), - )}(${inputVar});` + )}(${b->B.Val.var(input)});` }) } @@ -1450,9 +1502,9 @@ let transform: (t<'input>, s<'output> => transformDefinition<'input, 'output>) = make( ~name=schema.name, ~tagged=schema.tagged, - ~parseOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { let input = b->B.use(~schema, ~input, ~path) + switch transformer(EffectCtx.make(~selfSchema, ~path, ~operation=b.operation)) { | {parser, asyncParser: ?None} => b->B.embedSyncOperation(~input, ~fn=parser) | {parser: ?None, asyncParser} => b->B.embedAsyncOperation(~input, ~fn=asyncParser) @@ -1466,11 +1518,11 @@ let transform: (t<'input>, s<'output> => transformDefinition<'input, 'output>) = ) } }), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { switch transformer(EffectCtx.make(~selfSchema, ~path, ~operation=b.operation)) { | {serializer} => b->B.use(~schema, ~input=b->B.embedSyncOperation(~input, ~fn=serializer), ~path) + | {parser: ?None, asyncParser: ?None, serializer: ?None} => b->B.use(~schema, ~input, ~path) | {serializer: ?None, asyncParser: ?Some(_)} | {serializer: ?None, parser: ?Some(_)} => @@ -1510,29 +1562,14 @@ let rec preprocess = (schema, transformer) => { make( ~name=schema.name, ~tagged=schema.tagged, - ~parseOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { switch transformer(EffectCtx.make(~selfSchema, ~path, ~operation=b.operation)) { | {parser, asyncParser: ?None} => - let operationResultVar = b->B.var - b.code = b.code ++ `${operationResultVar}=${b->B.embedSyncOperation(~input, ~fn=parser)};` - b->B.useWithTypeFilter(~schema, ~input=operationResultVar, ~path) - | {parser: ?None, asyncParser} => { - let parseResultVar = b->B.embedAsyncOperation(~input, ~fn=asyncParser) - let outputVar = b->B.var - let asyncResultVar = b->B.varWithoutAllocation - - // TODO: Optimize async transformation to chain .then - b.code = - b.code ++ - `${outputVar}=()=>${parseResultVar}().then(${asyncResultVar}=>{${b->B.scope(b => { - let schemaOutputVar = - b->B.useWithTypeFilter(~schema, ~input=asyncResultVar, ~path) - let isAsync = schema.isAsyncParse->(Obj.magic: isAsyncParse => bool) - `return ${isAsync ? `${schemaOutputVar}()` : schemaOutputVar}` - })}});` - outputVar - } + b->B.useWithTypeFilter(~schema, ~input=b->B.embedSyncOperation(~input, ~fn=parser), ~path) + | {parser: ?None, asyncParser} => + b->B.transform(~input=b->B.embedAsyncOperation(~input, ~fn=asyncParser), (~input) => { + b->B.useWithTypeFilter(~schema, ~input, ~path) + }) | {parser: ?None, asyncParser: ?None} => b->B.useWithTypeFilter(~schema, ~input, ~path) | {parser: _, asyncParser: _} => b->B.invalidOperation( @@ -1541,9 +1578,9 @@ let rec preprocess = (schema, transformer) => { ) } }), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { let input = b->B.use(~schema, ~input, ~path) + switch transformer(EffectCtx.make(~selfSchema, ~path, ~operation=b.operation)) { | {serializer} => b->B.embedSyncOperation(~input, ~fn=serializer) // TODO: Test that it doesn't return InvalidOperation when parser is passed but not serializer @@ -1569,8 +1606,7 @@ let custom = (name, definer) => { ~name=() => name, ~metadataMap=Metadata.Map.empty, ~tagged=Unknown, - ~parseOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { switch definer(EffectCtx.make(~selfSchema, ~path, ~operation=b.operation)) { | {parser, asyncParser: ?None} => b->B.embedSyncOperation(~input, ~fn=parser) | {parser: ?None, asyncParser} => b->B.embedAsyncOperation(~input, ~fn=asyncParser) @@ -1584,8 +1620,7 @@ let custom = (name, definer) => { ) } }), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { switch definer(EffectCtx.make(~selfSchema, ~path, ~operation=b.operation)) { | {serializer} => b->B.embedSyncOperation(~input, ~fn=serializer) | {parser: ?None, asyncParser: ?None, serializer: ?None} => input @@ -1602,8 +1637,8 @@ let literal = value => { let value = value->castAnyToUnknown let literal = value->Literal.parse let internalLiteral = literal->Literal.toInternal - let operationBuilder = Builder.make((b, ~selfSchema as _, ~path) => { - let inputVar = b->B.useInputVar + let operationBuilder = Builder.make((b, ~input, ~selfSchema as _, ~path) => { + let inputVar = b->B.Val.var(input) b.code = b.code ++ `${b->internalLiteral.checkBuilder(~inputVar, ~literal)}||${b->B.raiseWithArg( @@ -1614,7 +1649,7 @@ let literal = value => { }), inputVar, )};` - inputVar + input }) make( ~name=() => `Literal(${literal->Literal.toString})`, @@ -1660,7 +1695,7 @@ module Definition = { module Variant = { @unboxed - type serializeOutput = Registered(string) | @as(0) Unregistered | @as(1) RegisteredMultipleTimes + type serializeOutput = Registered(val) | @as(0) Unregistered | @as(1) RegisteredMultipleTimes let factory = { (schema: t<'value>, definer: 'value => 'variant): t<'variant> => { @@ -1668,12 +1703,11 @@ module Variant = { make( ~name=schema.name, ~tagged=schema.tagged, - ~parseOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { - let input = b->B.useInput + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { b->B.embedSyncOperation(~input=b->B.use(~schema, ~input, ~path), ~fn=definer) }), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let inputVar = b->B.useInputVar + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { + let inputVar = b->B.Val.var(input) let definition = definer(symbol->(Obj.magic: Stdlib.Symbol.t => 'value))->( @@ -1688,15 +1722,15 @@ module Variant = { ) => { let kind = definition->Definition.toKindWithValue(~embeded=symbol) switch kind { - | Embeded => Registered(`${inputVar}${outputPath}`) + | Embeded => + Registered(outputPath === "" ? input : b->B.val(`${inputVar}${outputPath}`)) | Constant => { let constant = definition->Definition.toConstant - let constantVar = b->B.var + let constantVal = outputPath === "" ? input : b->B.val(`${inputVar}${outputPath}`) + let constantVar = b->B.Val.var(constantVal) b.code = b.code ++ - `${constantVar}=${inputVar}${outputPath};if(${constantVar}!==${b->B.embed( - constant, - )}){${b->B.raiseWithArg( + `if(${constantVar}!==${b->B.embed(constant)}){${b->B.raiseWithArg( ~path=path->Path.concat(outputPath), input => InvalidLiteral({ expected: constant->Literal.parse, @@ -1742,7 +1776,7 @@ module Variant = { | Registered(var) => b->B.use(~schema, ~input=var, ~path) | Unregistered => switch selfSchema->toInternalLiteral { - | Some(literal) => b->B.use(~schema, ~input=b->B.embed(literal.value), ~path) + | Some(literal) => b->B.use(~schema, ~input=b->B.val(b->B.embed(literal.value)), ~path) | None => b->B.invalidOperation( ~path, @@ -1768,47 +1802,53 @@ module Option = { let default = schema => schema->Metadata.get(~id=defaultMetadataId) - let parseOperationBuilder = Builder.make((b, ~selfSchema, ~path) => { - let inputVar = b->B.useInputVar - let outputVar = b->B.var - + let parseOperationBuilder = Builder.make((b, ~input, ~selfSchema, ~path) => { let isNull = %raw(`selfSchema.t.TAG === "Null"`) let childSchema = selfSchema.tagged->unsafeGetVariantPayload - let ifCode = b->B.scope(b => { - `${outputVar}=${b->B.use(~schema=childSchema, ~input=inputVar, ~path)}` - }) - let isAsync = childSchema.isAsyncParse->(Obj.magic: isAsyncParse => bool) + let bb = b->B.scope + let itemOutput = bb->B.use(~schema=childSchema, ~input, ~path) + let itemCode = bb->B.allocateScope - b.code = - b.code ++ - `if(${inputVar}!==${isNull - ? "null" - : "void 0"}){${ifCode}}else{${outputVar}=${switch isAsync { - | false => `void 0` - | true => `()=>Promise.resolve(void 0)` - }}}` - - outputVar + let isTransformed = isNull || itemOutput !== input + + let output = isTransformed ? {_scope: b, isAsync: itemOutput.isAsync} : input + + if itemCode !== "" || isTransformed { + b.code = + b.code ++ + `if(${b->B.Val.var(input)}!==${isNull ? "null" : "void 0"}){${itemCode}${b->B.Val.set( + output, + itemOutput, + )}}${isNull || output.isAsync ? `else{${b->B.Val.set(output, b->B.val(`void 0`))}}` : ""}` + } + + output }) - let serializeOperationBuilder = Builder.make((b, ~selfSchema, ~path) => { - let inputVar = b->B.useInputVar - let outputVar = b->B.var + let serializeOperationBuilder = Builder.make((b, ~input, ~selfSchema, ~path) => { + let output = b->B.allocateVal + let inputVar = b->B.Val.var(input) let isNull = %raw(`selfSchema.t.TAG === "Null"`) let childSchema = selfSchema.tagged->unsafeGetVariantPayload + let bb = b->B.scope + let itemOutput = + bb->B.use( + ~schema=childSchema, + ~input=bb->B.Val.map(bb->B.embed(%raw("Caml_option.valFromOption")), input), + ~path, + ) + let itemCode = bb->B.allocateScope + b.code = b.code ++ - `if(${inputVar}!==void 0){${b->B.scope(b => { - `${outputVar}=${b->B.use( - ~schema=childSchema, - ~input=`${b->B.embed(%raw("Caml_option.valFromOption"))}(${inputVar})`, - ~path, - )}` - })}}else{${outputVar}=${isNull ? `null` : `void 0`}}` - outputVar + `if(${inputVar}!==void 0){${itemCode}${b->B.Val.set(output, itemOutput)}}${isNull + ? `else{${b->B.Val.setInlined(output, `null`)}}` + : ""}` + + output }) let maybeTypeFilter = (~schema, ~inlinedNoneValue) => { @@ -1841,14 +1881,15 @@ module Option = { ~name=schema.name, ~metadataMap=schema.metadataMap->Metadata.Map.set(~id=defaultMetadataId, default), ~tagged=schema.tagged, - ~parseOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { - let input = b->B.useInput - b->B.transform(~input=b->B.use(~schema, ~input, ~path), ~isAsync=false, (b, ~input) => { - // TODO: Reassign input if it's not a var - `${input}===void 0?${switch default { - | Value(v) => b->B.embed(v) - | Callback(cb) => `${b->B.embed(cb)}()` - }}:${input}` + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { + b->B.transform(~input=b->B.use(~schema, ~input, ~path), (~input) => { + let inputVar = b->B.Val.var(input) + b->B.val( + `${inputVar}===void 0?${switch default { + | Value(v) => b->B.embed(v) + | Callback(cb) => `${b->B.embed(cb)}()` + }}:${inputVar}`, + ) }) }), ~serializeOperationBuilder=schema.serializeOperationBuilder, @@ -1897,7 +1938,7 @@ module Object = { let typeFilter = (~inputVar) => `!${inputVar}||${inputVar}.constructor!==Object` - let noopRefinement = (_b, ~selfSchema as _, ~inputVar as _, ~path as _) => () + let noopRefinement = (_b, ~input as _, ~selfSchema as _, ~path as _) => () let makeParseOperationBuilder = ( ~itemDefinitions, @@ -1906,17 +1947,21 @@ module Object = { ~inputRefinement, ~unknownKeysRefinement, ) => { - Builder.make((b, ~selfSchema, ~path) => { - let inputVar = b->B.useInputVar + Builder.make((b, ~input, ~selfSchema, ~path) => { + switch inputRefinement { + | Some(inputRefinement) => inputRefinement(b, ~input, ~selfSchema, ~path) + | None => () + } let registeredDefinitions = Stdlib.Set.empty() let asyncOutputVars = [] - inputRefinement(b, ~selfSchema, ~inputVar, ~path) + let b = b->B.scope + let inputVar = b->B.Val.var(input) let prevCode = b.code b.code = "" - unknownKeysRefinement(b, ~selfSchema, ~inputVar, ~path) + unknownKeysRefinement(b, ~input, ~selfSchema, ~path) let unknownKeysRefinementCode = b.code b.code = "" @@ -1928,19 +1973,20 @@ module Object = { let itemDefinition = definition->Definition.toEmbeded registeredDefinitions->Stdlib.Set.add(itemDefinition)->ignore let {schema, inputPath} = itemDefinition - let fieldOuputVar = + let fieldOuput = b->B.useWithTypeFilter( ~schema, - ~input=`${inputVar}${inputPath}`, + ~input=b->B.val(`${inputVar}${inputPath}`), ~path=path->Path.concat(inputPath), ) - let isAsyncField = schema.isAsyncParse->(Obj.magic: isAsyncParse => bool) - if isAsyncField { - // TODO: Ensure that it's not a var, but inlined - asyncOutputVars->Js.Array2.push(fieldOuputVar)->ignore - } - fieldOuputVar + if fieldOuput.isAsync { + let asyncOutputVar = b->B.Val.var(fieldOuput) + asyncOutputVars->Js.Array2.push(asyncOutputVar)->ignore + asyncOutputVar + } else { + b->B.Val.inline(fieldOuput) + } } | Constant => { let constant = definition->Definition.toConstant @@ -1975,16 +2021,14 @@ module Object = { let itemDefinition = itemDefinitions->Js.Array2.unsafe_get(idx) if registeredDefinitions->Stdlib.Set.has(itemDefinition)->not { let {schema, inputPath} = itemDefinition - let fieldOuputVar = + let fieldOuput = b->B.useWithTypeFilter( ~schema, - ~input=`${inputVar}${inputPath}`, + ~input=b->B.val(`${inputVar}${inputPath}`), ~path=path->Path.concat(inputPath), ) - let isAsyncField = schema.isAsyncParse->(Obj.magic: isAsyncParse => bool) - if isAsyncField { - // TODO: Ensure that it's not a var, but inlined - asyncOutputVars->Js.Array2.push(fieldOuputVar)->ignore + if fieldOuput.isAsync { + asyncOutputVars->Js.Array2.push(b->B.Val.var(fieldOuput))->ignore } } } @@ -1993,19 +2037,21 @@ module Object = { b.code = prevCode ++ unregisteredFieldsCode ++ registeredFieldsCode ++ unknownKeysRefinementCode - if asyncOutputVars->Js.Array2.length === 0 { - syncOutput + let val = if asyncOutputVars->Js.Array2.length === 0 { + b->B.val(syncOutput) } else { - let outputVar = b->B.var - b.code = - b.code ++ - `${outputVar}=()=>Promise.all([${asyncOutputVars + b->B.asyncVal( + `()=>Promise.all([${asyncOutputVars ->Js.Array2.map(asyncOutputVar => `${asyncOutputVar}()`) ->Js.Array2.joinWith( ",", - )}]).then(([${asyncOutputVars->Js.Array2.toString}])=>(${syncOutput}));` - outputVar + )}]).then(([${asyncOutputVars->Js.Array2.toString}])=>(${syncOutput}))`, + ) } + + let _ = b->B.exitScope + + val }) } @@ -2104,13 +2150,15 @@ module Object = { ~itemDefinitions, ~itemDefinitionsSet, ~definition, - ~inputRefinement=noopRefinement, - ~unknownKeysRefinement=(b, ~selfSchema, ~inputVar, ~path) => { + ~inputRefinement=None, + ~unknownKeysRefinement=(b, ~input, ~selfSchema, ~path) => { + let inputVar = b->B.Val.var(input) let withUnknownKeysRefinement = (selfSchema->classify->Obj.magic)["unknownKeys"] === Strict switch (withUnknownKeysRefinement, itemDefinitions) { | (true, []) => { - let keyVar = b->B.var + let key = b->B.allocateVal + let keyVar = b->B.Val.var(key) b.code = b.code ++ `for(${keyVar} in ${inputVar}){${b->B.raiseWithArg( @@ -2120,7 +2168,8 @@ module Object = { )}}` } | (true, _) => { - let keyVar = b->B.var + let key = b->B.allocateVal + let keyVar = b->B.Val.var(key) b.code = b.code ++ `for(${keyVar} in ${inputVar}){if(` for idx in 0 to itemDefinitions->Js.Array2.length - 1 { let itemDefinition = itemDefinitions->Js.Array2.unsafe_get(idx) @@ -2141,8 +2190,8 @@ module Object = { } }, ), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { - let inputVar = b->B.useInputVar + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { + let inputVar = b->B.Val.var(input) let fieldsCodeRef = ref("") let registeredDefinitions = Stdlib.Set.empty() @@ -2165,10 +2214,12 @@ module Object = { let {inlinedInputLocation, schema} = itemDefinition fieldsCodeRef.contents = fieldsCodeRef.contents ++ - `${inlinedInputLocation}:${b->B.use( - ~schema, - ~input=`${inputVar}${outputPath}`, - ~path=path->Path.concat(outputPath), + `${inlinedInputLocation}:${b->B.Val.inline( + b->B.use( + ~schema, + ~input=b->B.val(`${inputVar}${outputPath}`), + ~path=path->Path.concat(outputPath), + ), )},` } | Constant => { @@ -2219,7 +2270,7 @@ module Object = { } } - `{${fieldsCodeRef.contents}}` + b->B.val(`{${fieldsCodeRef.contents}}`) }), ~maybeTypeFilter=Some(typeFilter), ) @@ -2258,8 +2309,7 @@ module Object = { } module Never = { - let builder = Builder.make((b, ~selfSchema, ~path) => { - let input = b->B.useInput + let builder = Builder.make((b, ~input, ~selfSchema, ~path) => { b.code = b.code ++ b->B.raiseWithArg( @@ -2268,7 +2318,7 @@ module Never = { expected: selfSchema, received: input, }), - input, + b->B.Val.inline(input), ) ++ ";" input }) @@ -2349,8 +2399,8 @@ module String = { } schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}.length<${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}.length<${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Min({length: length}), @@ -2366,8 +2416,8 @@ module String = { } schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}.length>${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}.length>${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Max({length: length}), @@ -2383,8 +2433,8 @@ module String = { } schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}.length!==${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}.length!==${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Length({length: length}), @@ -2396,8 +2446,8 @@ module String = { let email = (schema, ~message=`Invalid email address`) => { schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(!${b->B.embed(emailRegex)}.test(${inputVar})){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(!${b->B.embed(emailRegex)}.test(${b->B.Val.var(input)})){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Email, @@ -2409,8 +2459,8 @@ module String = { let uuid = (schema, ~message=`Invalid UUID`) => { schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(!${b->B.embed(uuidRegex)}.test(${inputVar})){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(!${b->B.embed(uuidRegex)}.test(${b->B.Val.var(input)})){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Uuid, @@ -2422,8 +2472,8 @@ module String = { let cuid = (schema, ~message=`Invalid CUID`) => { schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(!${b->B.embed(cuidRegex)}.test(${inputVar})){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(!${b->B.embed(cuidRegex)}.test(${b->B.Val.var(input)})){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Cuid, @@ -2435,8 +2485,8 @@ module String = { let url = (schema, ~message=`Invalid url`) => { schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `try{new URL(${inputVar})}catch(_){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `try{new URL(${b->B.Val.var(input)})}catch(_){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Url, @@ -2448,11 +2498,13 @@ module String = { let pattern = (schema, re, ~message=`Invalid`) => { schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - let reVar = b->B.var - `${reVar}=${b->B.embed( - re, - )};${reVar}.lastIndex=0;if(!${reVar}.test(${inputVar})){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + let reVal = b->B.val(b->B.embed(re)) + let reVar = b->B.Val.var(reVal) + `${reVar}.lastIndex=0;if(!${reVar}.test(${b->B.Val.var(input)})){${b->B.fail( + ~message, + ~path, + )}}` }, ~refinement={ kind: Pattern({re: re}), @@ -2510,24 +2562,31 @@ module JsonString = { ~name=primitiveName, ~metadataMap=Metadata.Map.empty, ~tagged=String, - ~parseOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { - let input = b->B.useInput - let jsonVar = b->B.var + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { + let jsonVal = b->B.allocateVal + b.code = b.code ++ - `try{${jsonVar}=JSON.parse(${input})}catch(t){${b->B.raiseWithArg( + `try{${b->B.Val.set( + jsonVal, + b->B.Val.map("JSON.parse", input), + )}}catch(t){${b->B.raiseWithArg( ~path, message => OperationFailed(message), "t.message", )}}` - b->B.useWithTypeFilter(~schema, ~input=jsonVar, ~path) + let b = b->B.scope + let val = b->B.useWithTypeFilter(~schema, ~input=jsonVal, ~path) + let _ = b->B.exitScope + val }), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { - let input = b->B.useInput - `JSON.stringify(${b->B.use(~schema, ~input, ~path)}${space > 0 - ? `,null,${space->Stdlib.Int.unsafeToString}` - : ""})` + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { + b->B.val( + `JSON.stringify(${b->B.Val.inline(b->B.use(~schema, ~input, ~path))}${space > 0 + ? `,null,${space->Stdlib.Int.unsafeToString}` + : ""})`, + ) }), ~maybeTypeFilter=Some(String.typeFilter), ) @@ -2588,8 +2647,8 @@ module Int = { } schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}<${b->B.embed(minValue)}){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}<${b->B.embed(minValue)}){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Min({value: minValue}), @@ -2605,8 +2664,8 @@ module Int = { } schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}>${b->B.embed(maxValue)}){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}>${b->B.embed(maxValue)}){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Max({value: maxValue}), @@ -2618,8 +2677,8 @@ module Int = { let port = (schema, ~message="Invalid port") => { schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}<1||${inputVar}>65535){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}<1||${b->B.Val.var(input)}>65535){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Port, @@ -2669,8 +2728,8 @@ module Float = { } schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}<${b->B.embed(minValue)}){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}<${b->B.embed(minValue)}){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Min({value: minValue}), @@ -2686,8 +2745,8 @@ module Float = { } schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}>${b->B.embed(maxValue)}){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}>${b->B.embed(maxValue)}){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Max({value: maxValue}), @@ -2729,58 +2788,54 @@ module Array = { ~name=containerName, ~metadataMap=Metadata.Map.empty, ~tagged=Array(schema), - ~parseOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { - let inputVar = b->B.useInputVar + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { + let inputVar = b->B.Val.var(input) let iteratorVar = b->B.varWithoutAllocation - let outputVar = b->B.var + let output = b->B.val("[]") + + let bb = b->B.scope + let itemOutput = + bb->B.withPathPrepend(~path, ~dynamicLocationVar=iteratorVar, (b, ~path) => + b->B.useWithTypeFilter(~schema, ~input=b->B.val(`${inputVar}[${iteratorVar}]`), ~path) + ) + let itemCode = bb->B.allocateScope b.code = b.code ++ - `${outputVar}=[];for(let ${iteratorVar}=0;${iteratorVar}<${inputVar}.length;++${iteratorVar}){${b->B.scope( - b => { - let itemOutputVar = - b->B.withPathPrepend( - ~path, - ~dynamicLocationVar=iteratorVar, - (b, ~path) => - b->B.useWithTypeFilter(~schema, ~input=`${inputVar}[${iteratorVar}]`, ~path), - ) - `${outputVar}.push(${itemOutputVar})` - }, + `for(let ${iteratorVar}=0;${iteratorVar}<${inputVar}.length;++${iteratorVar}){${itemCode}${b->B.Val.push( + output, + itemOutput, )}}` - let isAsync = schema.isAsyncParse->(Obj.magic: isAsyncParse => bool) - if isAsync { - let asyncOutputVar = b->B.var - b.code = b.code ++ `${asyncOutputVar}=()=>Promise.all(${outputVar}.map(t=>t()));` - asyncOutputVar + if itemOutput.isAsync { + b->B.asyncVal(`()=>Promise.all(${b->B.Val.var(output)}.map(t=>t()))`) } else { - outputVar + output } }), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { if schema.serializeOperationBuilder === Builder.noop { - b->B.useInput + input } else { - let inputVar = b->B.useInputVar + let inputVar = b->B.Val.var(input) let iteratorVar = b->B.varWithoutAllocation - let outputVar = b->B.var + let output = b->B.val("[]") + + let bb = b->B.scope + let itemOutput = + bb->B.withPathPrepend(~path, ~dynamicLocationVar=iteratorVar, (b, ~path) => + b->B.use(~schema, ~input=b->B.val(`${inputVar}[${iteratorVar}]`), ~path) + ) + let itemCode = bb->B.allocateScope b.code = b.code ++ - `${outputVar}=[];for(let ${iteratorVar}=0;${iteratorVar}<${inputVar}.length;++${iteratorVar}){${b->B.scope( - b => { - let itemOutputVar = - b->B.withPathPrepend( - ~path, - ~dynamicLocationVar=iteratorVar, - (b, ~path) => b->B.use(~schema, ~input=`${inputVar}[${iteratorVar}]`, ~path), - ) - `${outputVar}.push(${itemOutputVar})` - }, + `for(let ${iteratorVar}=0;${iteratorVar}<${inputVar}.length;++${iteratorVar}){${itemCode}${b->B.Val.push( + output, + itemOutput, )}}` - outputVar + output } }), ~maybeTypeFilter=Some(typeFilter), @@ -2794,8 +2849,8 @@ module Array = { } schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}.length<${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}.length<${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Min({length: length}), @@ -2811,8 +2866,8 @@ module Array = { } schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}.length>${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}.length>${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Max({length: length}), @@ -2828,8 +2883,8 @@ module Array = { } schema->addRefinement( ~metadataId=Refinement.metadataId, - ~refiner=(b, ~inputVar, ~selfSchema as _, ~path) => { - `if(${inputVar}.length!==${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` + ~refiner=(b, ~input, ~selfSchema as _, ~path) => { + `if(${b->B.Val.var(input)}.length!==${b->B.embed(length)}){${b->B.fail(~message, ~path)}}` }, ~refinement={ kind: Length({length: length}), @@ -2846,61 +2901,63 @@ module Dict = { ~name=containerName, ~metadataMap=Metadata.Map.empty, ~tagged=Dict(schema), - ~parseOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { - let inputVar = b->B.useInputVar + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { + let inputVar = b->B.Val.var(input) let keyVar = b->B.varWithoutAllocation - let outputVar = b->B.var + + let bb = b->B.scope + let itemInput = bb->B.val(`${inputVar}[${keyVar}]`) + let itemOutput = + bb->B.withPathPrepend(~path, ~dynamicLocationVar=keyVar, (b, ~path) => + b->B.useWithTypeFilter(~schema, ~input=itemInput, ~path) + ) + let itemCode = bb->B.allocateScope + let isTransformed = itemInput !== itemOutput // FIXME: Make withPathPrepend return not transformed val + let output = isTransformed ? b->B.val("{}") : input b.code = b.code ++ - `${outputVar}={};for(let ${keyVar} in ${inputVar}){${b->B.scope(b => { - let itemOutputVar = - b->B.withPathPrepend( - ~path, - ~dynamicLocationVar=keyVar, - (b, ~path) => - b->B.useWithTypeFilter(~schema, ~input=`${inputVar}[${keyVar}]`, ~path), - ) - `${outputVar}[${keyVar}]=${itemOutputVar}` - })}}` + `for(let ${keyVar} in ${inputVar}){${itemCode}${isTransformed + ? b->B.Val.addKey(output, keyVar, itemOutput) + : ""}}` - let isAsync = schema.isAsyncParse->(Obj.magic: isAsyncParse => bool) - if isAsync { + if itemOutput.isAsync { let resolveVar = b->B.varWithoutAllocation let rejectVar = b->B.varWithoutAllocation let asyncParseResultVar = b->B.varWithoutAllocation let counterVar = b->B.varWithoutAllocation - let asyncOutputVar = b->B.var - b.code = - b.code ++ - `${asyncOutputVar}=()=>new Promise((${resolveVar},${rejectVar})=>{let ${counterVar}=Object.keys(${outputVar}).length;for(let ${keyVar} in ${outputVar}){${outputVar}[${keyVar}]().then(${asyncParseResultVar}=>{${outputVar}[${keyVar}]=${asyncParseResultVar};if(${counterVar}--===1){${resolveVar}(${outputVar})}},${rejectVar})}});` - asyncOutputVar + let outputVar = b->B.Val.var(output) + b->B.asyncVal( + `()=>new Promise((${resolveVar},${rejectVar})=>{let ${counterVar}=Object.keys(${outputVar}).length;for(let ${keyVar} in ${outputVar}){${outputVar}[${keyVar}]().then(${asyncParseResultVar}=>{${outputVar}[${keyVar}]=${asyncParseResultVar};if(${counterVar}--===1){${resolveVar}(${outputVar})}},${rejectVar})}})`, + ) } else { - outputVar + output } }), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { if schema.serializeOperationBuilder === Builder.noop { - b->B.useInput + input } else { - let inputVar = b->B.useInputVar + let inputVar = b->B.Val.var(input) + let output = b->B.val("{}") let keyVar = b->B.varWithoutAllocation - let outputVar = b->B.var + + let bb = b->B.scope + let itemOutput = + bb->B.withPathPrepend(~path, ~dynamicLocationVar=keyVar, (b, ~path) => + b->B.use(~schema, ~input=b->B.val(`${inputVar}[${keyVar}]`), ~path) + ) + let itemCode = bb->B.allocateScope b.code = b.code ++ - `${outputVar}={};for(let ${keyVar} in ${inputVar}){${b->B.scope(b => { - let itemOutputVar = - b->B.withPathPrepend( - ~path, - ~dynamicLocationVar=keyVar, - (b, ~path) => b->B.use(~schema, ~input=`${inputVar}[${keyVar}]`, ~path), - ) - - `${outputVar}[${keyVar}]=${itemOutputVar}` - })}}` + `for(let ${keyVar} in ${inputVar}){${itemCode}${b->B.Val.addKey( + output, + keyVar, + itemOutput, + )}}` - outputVar + output } }), ~maybeTypeFilter=Some(Object.typeFilter), @@ -2995,25 +3052,27 @@ module Tuple = { ~itemDefinitions, ~itemDefinitionsSet, ~definition, - ~inputRefinement=(b, ~selfSchema as _, ~inputVar, ~path) => { - b.code = - b.code ++ - `if(${inputVar}.length!==${length->Stdlib.Int.unsafeToString}){${b->B.raiseWithArg( - ~path, - numberOfInputItems => InvalidTupleSize({ - expected: length, - received: numberOfInputItems, - }), - `${inputVar}.length`, - )}}` - }, + ~inputRefinement=Some( + (b, ~input, ~selfSchema as _, ~path) => { + let inputVar = b->B.Val.var(input) + + b.code = + b.code ++ + `if(${inputVar}.length!==${length->Stdlib.Int.unsafeToString}){${b->B.raiseWithArg( + ~path, + numberOfInputItems => InvalidTupleSize({ + expected: length, + received: numberOfInputItems, + }), + `${inputVar}.length`, + )}}` + }, + ), ~unknownKeysRefinement=Object.noopRefinement, ), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { - let inputVar = b->B.useInputVar - let outputVar = b->B.var + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { + let output = b->B.val("[]") let registeredDefinitions = Stdlib.Set.empty() - b.code = b.code ++ `${outputVar}=[];` { let prevCode = b.code @@ -3035,23 +3094,27 @@ module Tuple = { registeredDefinitions->Stdlib.Set.add(itemDefinition)->ignore let {schema, inputPath} = itemDefinition let fieldOuputVar = - b->B.use( - ~schema, - ~input=`${inputVar}${outputPath}`, - ~path=path->Path.concat(outputPath), + b->B.Val.inline( + b->B.use( + ~schema, + ~input=b->B.val(`${b->B.Val.var(input)}${outputPath}`), + ~path=path->Path.concat(outputPath), + ), ) - b.code = b.code ++ `${outputVar}${inputPath}=${fieldOuputVar};` + b.code = b.code ++ `${b->B.Val.var(output)}${inputPath}=${fieldOuputVar};` } | Constant => { let value = definition->Definition.toConstant b.code = - `if(${inputVar}${outputPath}!==${b->B.embed(value)}){${b->B.raiseWithArg( + `if(${b->B.Val.var(input)}${outputPath}!==${b->B.embed( + value, + )}){${b->B.raiseWithArg( ~path=path->Path.concat(outputPath), input => InvalidLiteral({ expected: value->Literal.parse, received: input, }), - `${inputVar}${outputPath}`, + `${b->B.Val.var(input)}${outputPath}`, )}}` ++ b.code } @@ -3079,7 +3142,7 @@ module Tuple = { let {schema, inlinedInputLocation, inputPath} = itemDefinition switch schema->toInternalLiteral { | Some(literal) => - b.code = b.code ++ `${outputVar}${inputPath}=${b->B.embed(literal.value)};` + b.code = b.code ++ `${b->B.Val.var(output)}${inputPath}=${b->B.embed(literal.value)};` | None => b->B.invalidOperation( ~path, @@ -3089,7 +3152,7 @@ module Tuple = { } } - outputVar + output }), ~maybeTypeFilter=Some(Array.typeFilter), ~metadataMap=Metadata.Map.empty, @@ -3109,153 +3172,116 @@ module Union = { ~name=() => `Union(${schemas->Js.Array2.map(s => s.name())->Js.Array2.joinWith(", ")})`, ~metadataMap=Metadata.Map.empty, ~tagged=Union(schemas), - ~parseOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let inputVar = b->B.useInputVar + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { let schemas = selfSchema->classify->unsafeGetVariantPayload - let isAsyncRef = ref(false) - let itemsCode = [] - let itemsOutputVar = [] - - let prevCode = b.code - for idx in 0 to schemas->Js.Array2.length - 1 { - let schema = schemas->Js.Array2.unsafe_get(idx) - b.code = "" - let itemOutputVar = b->B.withBuildErrorInline( - () => { - b->B.useWithTypeFilter( - // A hack to bypass an additional function wrapping for var context optimisation - ~schema=%raw(`schema`), - ~input=inputVar, - ~path=Path.empty, - ) - }, - ) - let isAsyncItem = schema.isAsyncParse->(Obj.magic: isAsyncParse => bool) - if isAsyncItem { - isAsyncRef.contents = true - } - itemsOutputVar->Js.Array2.push(itemOutputVar)->ignore - itemsCode->Js.Array2.push(b.code)->ignore - } - b.code = prevCode - let isAsync = isAsyncRef.contents - - let outputVar = b->B.var - + let output = b->B.allocateVal let codeEndRef = ref("") let errorCodeRef = ref("") + let isAsync = ref(false) - // TODO: Use B.withCatch ??? + // TODO: Add support for async for idx in 0 to schemas->Js.Array2.length - 1 { - let schema = schemas->Js.Array2.unsafe_get(idx) - let code = itemsCode->Js.Array2.unsafe_get(idx) - let itemOutputVar = itemsOutputVar->Js.Array2.unsafe_get(idx) - let isAsyncItem = schema.isAsyncParse->(Obj.magic: isAsyncParse => bool) - - let errorVar = b->B.varWithoutAllocation - - let errorCode = if isAsync { - (isAsyncItem ? `${errorVar}===${itemOutputVar}?${errorVar}():` : "") ++ - `Promise.reject(${errorVar})` - } else { - errorVar - } - if idx === 0 { - errorCodeRef.contents = errorCode - } else { - errorCodeRef.contents = errorCodeRef.contents ++ "," ++ errorCode + let prevCode = b.code + try { + let schema = schemas->Js.Array2.unsafe_get(idx) + let errorVar = `e` ++ idx->Stdlib.Int.unsafeToString + b.code = b.code ++ `try{` + let itemOutput = b->B.useWithTypeFilter(~schema, ~input, ~path=Path.empty) + if itemOutput.isAsync { + isAsync := true + } + + b.code = b.code ++ `${b->B.Val.set(output, itemOutput)}}catch(${errorVar}){` + codeEndRef.contents = codeEndRef.contents ++ "}" + + errorCodeRef := errorCodeRef.contents ++ errorVar ++ "," + } catch { + | exn => + errorCodeRef := + errorCodeRef.contents ++ b->B.embed(exn->InternalError.getOrRethrow) ++ "," + b.code = prevCode } + } - b.code = - b.code ++ - `try{${code}${switch (isAsyncItem, isAsync) { - | (true, _) => `throw ${itemOutputVar}` - | (false, false) => `${outputVar}=${itemOutputVar}` - | (false, true) => `${outputVar}=()=>Promise.resolve(${itemOutputVar})` - }}}catch(${errorVar}){if(${b->B.isInternalError(errorVar)}${isAsyncItem - ? `||${errorVar}===${itemOutputVar}` - : ""}){` - codeEndRef.contents = `}else{throw ${errorVar}}}` ++ codeEndRef.contents + if isAsync.contents { + b->B.invalidOperation( + ~path, + ~description="S.union doesn't support async items. Please create an issue to rescript-schema if you nead the feature.", + ) } - if isAsync { - b.code = - b.code ++ - `${outputVar}=()=>Promise.any([${errorCodeRef.contents}]).catch(t=>{${b->B.raiseWithArg( - ~path, - internalErrors => { - InvalidUnion(internalErrors) - }, - `t.errors`, - )}})` ++ - codeEndRef.contents - outputVar + b.code = + b.code ++ + b->B.raiseWithArg( + ~path, + internalErrors => { + InvalidUnion(internalErrors) + }, + `[${errorCodeRef.contents}]`, + ) ++ + codeEndRef.contents + + let isAllSchemasBuilderFailed = codeEndRef.contents === "" + if isAllSchemasBuilderFailed { + b.code = b.code ++ ";" + input } else { - b.code = - b.code ++ - b->B.raiseWithArg( - ~path, - internalErrors => InvalidUnion(internalErrors), - `[${errorCodeRef.contents}]`, - ) ++ - codeEndRef.contents - outputVar + output } }), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let inputVar = b->B.useInputVar + ~serializeOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { let schemas = selfSchema->classify->unsafeGetVariantPayload - let outputVar = b->B.var - + let output = b->B.allocateVal let codeEndRef = ref("") - let errorVarsRef = ref("") + let errorCodeRef = ref("") for idx in 0 to schemas->Js.Array2.length - 1 { - let itemSchema = schemas->Js.Array2.unsafe_get(idx) - let errorVar = b->B.varWithoutAllocation - errorVarsRef.contents = errorVarsRef.contents ++ errorVar ++ `,` - - b.code = - b.code ++ - `try{${b->B.scope( - b => { - let itemOutput = - b->B.withBuildErrorInline( - () => b->B.use(~schema=itemSchema, ~input=inputVar, ~path=Path.empty), - ) - let itemOutput = switch itemSchema.maybeTypeFilter { - | Some(typeFilter) => - let itemOutputVar = b->B.toVar(itemOutput) - b.code = - b.code ++ - b->B.typeFilterCode( - ~schema=itemSchema, - ~typeFilter, - ~inputVar=itemOutputVar, - ~path=Path.empty, - ) - itemOutputVar - | None => itemOutput - } - `${outputVar}=${itemOutput}` - }, - )}}catch(${errorVar}){if(${b->B.isInternalError(errorVar)}){` - - codeEndRef.contents = `}else{throw ${errorVar}}}` ++ codeEndRef.contents + let prevCode = b.code + try { + let schema = schemas->Js.Array2.unsafe_get(idx) + let errorVar = `e` ++ idx->Stdlib.Int.unsafeToString + b.code = b.code ++ `try{` + let itemOutput = b->B.use(~schema, ~input, ~path=Path.empty) + b.code = + b.code ++ + switch schema.maybeTypeFilter { + | Some(typeFilter) => + b->B.typeFilterCode(~schema, ~typeFilter, ~input=itemOutput, ~path=Path.empty) + | None => "" + } ++ + `${b->B.Val.set(output, itemOutput)}}catch(${errorVar}){` + codeEndRef.contents = codeEndRef.contents ++ "}" + + errorCodeRef := errorCodeRef.contents ++ errorVar ++ "," + } catch { + | exn => { + errorCodeRef := + errorCodeRef.contents ++ b->B.embed(exn->InternalError.getOrRethrow) ++ "," + b.code = prevCode + } + } } b.code = b.code ++ b->B.raiseWithArg( ~path, - internalErrors => InvalidUnion(internalErrors), - `[${errorVarsRef.contents}]`, + internalErrors => { + InvalidUnion(internalErrors) + }, + `[${errorCodeRef.contents}]`, ) ++ codeEndRef.contents - outputVar + let isAllSchemasBuilderFailed = codeEndRef.contents === "" + if isAllSchemasBuilderFailed { + b.code = b.code ++ ";" + input + } else { + output + } }), ~maybeTypeFilter=None, ) @@ -3279,7 +3305,7 @@ let json = (~validate) => ~metadataMap=Metadata.Map.empty, ~maybeTypeFilter=None, ~parseOperationBuilder=validate - ? Builder.make((b, ~selfSchema, ~path) => { + ? Builder.make((b, ~input, ~selfSchema, ~path) => { let rec parse = (input, ~path=path) => { switch input->Stdlib.Type.typeof { | #number if Js.Float.isNaN(input->(Obj.magic: unknown => float))->not => @@ -3331,9 +3357,8 @@ let json = (~validate) => ) } } - let input = b->B.useInput - `${b->B.embed(parse)}(${input})` + b->B.Val.map(b->B.embed(parse), input) }) : Builder.noop, ) @@ -3350,27 +3375,30 @@ let catch = (schema, getFallbackValue) => { let schema = schema->toUnknown make( ~name=schema.name, - ~parseOperationBuilder=Builder.make((b, ~selfSchema, ~path) => { - let inputVar = b->B.useInputVar + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema, ~path) => { + let inputVar = b->B.Val.var(input) + b->B.withCatch( ~catch=(b, ~errorVar) => Some( - `${b->B.embed((input, internalError) => - getFallbackValue({ - Catch.input, - error: internalError, - schema: selfSchema->castUnknownSchemaToAnySchema, - fail: (message, ~path as customPath=Path.empty) => { - InternalError.raise( - ~path=path->Path.concat(customPath), - ~code=OperationFailed(message), - ~operation=b.operation, - ) - }, - }) - )}(${inputVar},${errorVar})`, + b->B.val( + `${b->B.embed((input, internalError) => + getFallbackValue({ + Catch.input, + error: internalError, + schema: selfSchema->castUnknownSchemaToAnySchema, + fail: (message, ~path as customPath=Path.empty) => { + InternalError.raise( + ~path=path->Path.concat(customPath), + ~code=OperationFailed(message), + ~operation=b.operation, + ) + }, + }) + )}(${inputVar},${errorVar})`, + ), ), b => { - b->B.useWithTypeFilter(~schema, ~input=inputVar, ~path) + b->B.useWithTypeFilter(~schema, ~input, ~path) }, ) }), @@ -3946,15 +3974,14 @@ let js_merge = (s1, s2) => { fieldNames, fields, }), - ~parseOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { - let inputVar = b->B.useInputVar - let s1Result = b->B.use(~schema=s1, ~input=inputVar, ~path) - let s2Result = b->B.use(~schema=s2, ~input=inputVar, ~path) + ~parseOperationBuilder=Builder.make((b, ~input, ~selfSchema as _, ~path) => { + let s1Result = b->B.use(~schema=s1, ~input, ~path) + let s2Result = b->B.use(~schema=s2, ~input, ~path) // TODO: Check that these are objects // TODO: Check that s1Result is not mutating input - `Object.assign(${s1Result}, ${s2Result})` + b->B.val(`Object.assign(${b->B.Val.inline(s1Result)}, ${b->B.Val.inline(s2Result)})`) }), - ~serializeOperationBuilder=Builder.make((b, ~selfSchema as _, ~path) => { + ~serializeOperationBuilder=Builder.make((b, ~input as _, ~selfSchema as _, ~path) => { b->B.invalidOperation(~path, ~description=`The S.merge serializing is not supported yet`) }), ~maybeTypeFilter=Some(Object.typeFilter),