Skip to content

Commit

Permalink
Improve serializing of option schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
DZakh committed Sep 10, 2024
1 parent 39b79fe commit f4989bd
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 112 deletions.
2 changes: 1 addition & 1 deletion packages/tests/src/core/Example_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,6 @@ test("Compiled serialize code snapshot", t => {
t->U.assertCompiledCode(
~schema=filmSchema,
~op=#Serialize,
`i=>{let v0=i["tags"],v3=i["rating"],v4=i["deprecatedAgeRestriction"],v5;if(v3!=="G"){if(v3!=="PG"){if(v3!=="PG13"){if(v3!=="R"){e[0](v3)}}}}if(v4!==void 0){v5=e[1](v4)}return {"Id":i["id"],"Title":i["title"],"Tags":v0,"Rating":v3,"Age":v5,}}`,
`i=>{let v0=i["tags"],v3=i["rating"];if(v3!=="G"){if(v3!=="PG"){if(v3!=="PG13"){if(v3!=="R"){e[0](v3)}}}}return {"Id":i["id"],"Title":i["title"],"Tags":v0,"Rating":v3,"Age":i["deprecatedAgeRestriction"],}}`,
)
})
7 changes: 4 additions & 3 deletions packages/tests/src/core/S_array_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,19 @@ module CommonWithNested = {

test("Compiled serialize code snapshot", t => {
let schema = S.array(S.string)
t->U.assertCompiledCodeIsNoop(~schema, ~op=#Serialize)

let schema = S.array(S.option(S.string))
t->U.assertCompiledCodeIsNoop(~schema, ~op=#Serialize)
})

test("Compiled serialize code snapshot with transform", t => {
let schema = S.array(S.option(S.string))
let schema = S.array(S.null(S.string))

// TODO: Simplify
t->U.assertCompiledCode(
~schema,
~op=#Serialize,
`i=>{let v5=[];for(let v0=0;v0<i.length;++v0){let v2=i[v0],v4;try{let 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.push(v4)}return v5}`,
`i=>{let v5=[];for(let v0=0;v0<i.length;++v0){let v2=i[v0],v4;try{let v3;if(v2!==void 0){v3=v2}else{v3=null}v4=v3}catch(v1){if(v1&&v1.s===s){v1.path=""+\'["\'+v0+\'"]\'+v1.path}throw v1}v5.push(v4)}return v5}`,
)
})

Expand Down
6 changes: 4 additions & 2 deletions packages/tests/src/core/S_dict_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,19 @@ module CommonWithNested = {

test("Compiled serialize code snapshot", t => {
let schema = S.dict(S.string)
t->U.assertCompiledCodeIsNoop(~schema, ~op=#Serialize)

let schema = S.dict(S.option(S.string))
t->U.assertCompiledCodeIsNoop(~schema, ~op=#Serialize)
})

test("Compiled serialize code snapshot with transform", t => {
let schema = S.dict(S.option(S.string))
let schema = S.dict(S.null(S.string))

t->U.assertCompiledCode(
~schema,
~op=#Serialize,
`i=>{let v5={};for(let v0 in i){let v2=i[v0],v4;try{let 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}`,
`i=>{let v5={};for(let v0 in i){let v2=i[v0],v4;try{let v3;if(v2!==void 0){v3=v2}else{v3=null}v4=v3}catch(v1){if(v1&&v1.s===s){v1.path=""+\'["\'+v0+\'"]\'+v1.path}throw v1}v5[v0]=v4}return v5}`,
)
})

Expand Down
25 changes: 16 additions & 9 deletions packages/tests/src/core/S_null_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,12 @@ module Common = {
)
})

let serializeCode = `let v0;if(i!==void 0){v0=i}else{v0=null}return v0`
test("Compiled serialize code snapshot", t => {
let schema = factory()
t->U.assertCompiledCode(~schema, ~op=#Serialize, `i=>{${serializeCode}}`)
})

test("Compiled reverse code snapshot", t => {
let schema = factory()
t->U.assertCompiledCode(
~schema=schema->S.\"~experimantalReverse",
~op=#Parse,
`i=>{if(i!==void 0&&(typeof i!=="string")){e[0](i)}${serializeCode}}`,
~schema,
~op=#Serialize,
`i=>{let v0;if(i!==void 0){v0=i}else{v0=null}return v0}`,
)
})

Expand Down Expand Up @@ -163,3 +157,16 @@ test("Serializes Some(None) to null for null nested in option", t => {
`i=>{let v2;if(i!==void 0){let v0=e[0](i),v1;if(v0!==void 0){v1=v0}else{v1=null}v2=v1}return v2}`,
)
})

test("Serializes Some(None) to null for null nested in null", t => {
let schema = S.null(S.null(S.bool))

t->Assert.deepEqual(Some(None)->S.serializeToUnknownWith(schema), Ok(%raw(`null`)), ())
t->Assert.deepEqual(None->S.serializeToUnknownWith(schema), Ok(%raw(`null`)), ())

t->U.assertCompiledCode(
~schema,
~op=#Serialize,
`i=>{let v2;if(i!==void 0){let v0=e[0](i),v1;if(v0!==void 0){v1=v0}else{v1=null}v2=v1}else{v2=null}return v2}`,
)
})
53 changes: 48 additions & 5 deletions packages/tests/src/core/S_option_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,7 @@ module Common = {
test("Compiled serialize code snapshot", t => {
let schema = factory()

t->U.assertCompiledCode(
~schema,
~op=#Serialize,
`i=>{let v0;if(i!==void 0){v0=e[0](i)}return v0}`,
)
t->U.assertCompiledCodeIsNoop(~schema, ~op=#Serialize)
})

test("Reverse to self", t => {
Expand Down Expand Up @@ -126,4 +122,51 @@ test("Serializes Some(None) to undefined for option nested in null", t => {

t->Assert.deepEqual(Some(None)->S.serializeToUnknownWith(schema), Ok(%raw(`undefined`)), ())
t->Assert.deepEqual(None->S.serializeToUnknownWith(schema), Ok(%raw(`null`)), ())

t->U.assertCompiledCode(
~schema,
~op=#Serialize,
`i=>{let v0;if(i!==void 0){v0=e[0](i)}else{v0=null}return v0}`,
)
})

test("Applies valFromOption for Some()", t => {
let schema = S.option(S.literal())

t->Assert.deepEqual(Some()->S.serializeToUnknownWith(schema), Ok(%raw(`undefined`)), ())
t->Assert.deepEqual(None->S.serializeToUnknownWith(schema), Ok(%raw(`undefined`)), ())

t->U.assertCompiledCode(
~schema,
~op=#Serialize,
`i=>{let v0;if(i!==void 0){v0=e[0](i)}return v0}`,
)
})

test("Doesn't apply valFromOption for non-undefined literals in option", t => {
let schema: S.t<option<Js.Null.t<unknown>>> = S.option(S.literal(%raw(`null`)))

// Note: It'll fail without a type annotation, but we can't do anything here
t->Assert.deepEqual(Some(%raw(`null`))->S.serializeToUnknownWith(schema), Ok(%raw(`null`)), ())
t->Assert.deepEqual(None->S.serializeToUnknownWith(schema), Ok(%raw(`undefined`)), ())

t->U.assertCompiledCodeIsNoop(~schema, ~op=#Serialize)
})

test("Applies valFromOption for unknown in option", t => {
let schema = S.option(S.unknown)

t->Assert.deepEqual(
Some(%raw(`undefined`))->S.serializeToUnknownWith(schema),
Ok(%raw(`undefined`)),
(),
)
t->Assert.deepEqual(Some(%raw(`"foo"`))->S.serializeToUnknownWith(schema), Ok(%raw(`"foo"`)), ())
t->Assert.deepEqual(None->S.serializeToUnknownWith(schema), Ok(%raw(`undefined`)), ())

t->U.assertCompiledCode(
~schema,
~op=#Serialize,
`i=>{let v0;if(i!==void 0){v0=e[0](i)}return v0}`,
)
})
2 changes: 1 addition & 1 deletion packages/tests/src/core/S_union_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ module CknittelBugReport = {
t->U.assertCompiledCode(
~schema,
~op=#Serialize,
`i=>{let v2=i;try{let v0=i["_0"]["payload"]["a"],v1;if(i["TAG"]!=="A"){e[0](i["TAG"])}if(v0!==void 0){v1=e[1](v0)}v2={"payload":{"a":v1,},}}catch(e0){try{let v3=i["_0"]["payload"]["b"],v4;if(i["TAG"]!=="B"){e[2](i["TAG"])}if(v3!==void 0){v4=e[3](v3)}v2={"payload":{"b":v4,},}}catch(e1){e[4]([e0,e1,])}}return v2}`,
`i=>{let v0=i;try{if(i["TAG"]!=="A"){e[0](i["TAG"])}v0={"payload":{"a":i["_0"]["payload"]["a"],},}}catch(e0){try{if(i["TAG"]!=="B"){e[1](i["TAG"])}v0={"payload":{"b":i["_0"]["payload"]["b"],},}}catch(e1){e[2]([e0,e1,])}}return v0}`,
)

let x = {
Expand Down
76 changes: 40 additions & 36 deletions src/S_Core.bs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1214,34 +1214,34 @@ function $$default(schema) {
return schema.m[defaultMetadataId];
}

function builder(b, input, selfSchema, path) {
var isNullInput = selfSchema.t.TAG === "Null";
var reversed = selfSchema.r();
var isNullOutput = reversed.t.TAG === "Null";
var childSchema = selfSchema.t._0;
var bb = scope(b);
var itemInput;
if (!isNullOutput && (b.g.o === "SerializeToJson" || b.g.o === "SerializeToUnknown")) {
var value = Caml_option.valFromOption;
itemInput = val(bb, "e[" + (bb.g.e.push(value) - 1) + "](" + $$var(b, input) + ")");
} else {
itemInput = input;
}
var itemOutput = childSchema.b(bb, itemInput, childSchema, path);
var itemCode = allocateScope(bb);
var inputLiteral = isNullInput ? "null" : "void 0";
var ouputLiteral = isNullOutput ? "null" : "void 0";
var isTransformed = inputLiteral !== ouputLiteral || itemOutput !== input;
var output = isTransformed ? ({
s: b,
a: itemOutput.a
}) : input;
if (itemCode !== "" || isTransformed) {
b.c = b.c + ("if(" + $$var(b, input) + "!==" + inputLiteral + "){" + itemCode + set(b, output, itemOutput) + "}" + (
inputLiteral !== ouputLiteral || output.a ? "else{" + set(b, output, val(b, ouputLiteral)) + "}" : ""
));
}
return output;
function makeBuilder(isNullInput, isNullOutput) {
return function (b, input, selfSchema, path) {
var childSchema = selfSchema.t._0;
var childSchemaTag = childSchema.t.TAG;
var bb = scope(b);
var itemInput;
if ((b.g.o === "SerializeToJson" || b.g.o === "SerializeToUnknown") && (childSchema.t === "Unknown" || childSchemaTag === "Option" || childSchemaTag === "Literal" && childSchema.t._0.value === (void 0))) {
var value = Caml_option.valFromOption;
itemInput = val(bb, "e[" + (bb.g.e.push(value) - 1) + "](" + $$var(b, input) + ")");
} else {
itemInput = input;
}
var itemOutput = childSchema.b(bb, itemInput, childSchema, path);
var itemCode = allocateScope(bb);
var inputLiteral = isNullInput ? "null" : "void 0";
var ouputLiteral = isNullOutput ? "null" : "void 0";
var isTransformed = inputLiteral !== ouputLiteral || itemOutput !== input;
var output = isTransformed ? ({
s: b,
a: itemOutput.a
}) : input;
if (itemCode !== "" || isTransformed) {
b.c = b.c + ("if(" + $$var(b, input) + "!==" + inputLiteral + "){" + itemCode + set(b, output, itemOutput) + "}" + (
inputLiteral !== ouputLiteral || output.a ? "else{" + set(b, output, val(b, ouputLiteral)) + "}" : ""
));
}
return output;
};
}

function maybeTypeFilter(schema, inlinedNoneValue) {
Expand All @@ -1258,7 +1258,7 @@ function factory(schema) {
return makeSchema(containerName, {
TAG: "Option",
_0: schema
}, empty, builder, maybeTypeFilter(schema, "void 0"), onlyChild(factory, schema));
}, empty, makeBuilder(false, false), maybeTypeFilter(schema, "void 0"), onlyChild(factory, schema));
}

function getWithDefault(schema, $$default) {
Expand Down Expand Up @@ -1301,16 +1301,20 @@ function factory$1(schema) {
return makeSchema(containerName, {
TAG: "Null",
_0: schema
}, empty, builder, maybeTypeFilter(schema, "null"), (function () {
return factory(schema.r());
}, empty, makeBuilder(true, false), maybeTypeFilter(schema, "null"), (function () {
var child = schema.r();
return makeReverseSchema(containerName, {
TAG: "Option",
_0: child
}, empty, makeBuilder(false, true), maybeTypeFilter(schema, "void 0"));
}));
}

function nullable(schema) {
return factory(factory$1(schema));
}

function builder$1(b, input, selfSchema, path) {
function builder(b, input, selfSchema, path) {
b.c = b.c + failWithArg(b, path, (function (input) {
return {
TAG: "InvalidType",
Expand All @@ -1321,7 +1325,7 @@ function builder$1(b, input, selfSchema, path) {
return input;
}

var schema = makeSchema(primitiveName, "Never", empty, builder$1, undefined, toSelf);
var schema = makeSchema(primitiveName, "Never", empty, builder, undefined, toSelf);

var metadataId = "rescript-schema:Array.refinements";

Expand Down Expand Up @@ -1372,7 +1376,7 @@ function typeFilter$1(_b, inputVar) {
return "!" + inputVar + "||" + inputVar + ".constructor!==Object";
}

function builder$2(b, input, selfSchema, path) {
function builder$1(b, input, selfSchema, path) {
var asyncOutputs = [];
var outputs = new WeakMap();
var parseItems = function (b, input, schema, path) {
Expand Down Expand Up @@ -1624,7 +1628,7 @@ function factory$3(definer) {
},
n: name,
r: reverse,
b: builder$2,
b: builder$1,
f: typeFilter$1,
i: 0,
d: definer,
Expand Down Expand Up @@ -1693,7 +1697,7 @@ function tuple(definer) {
TAG: "Tuple",
items: items,
definition: definition
}, empty, builder$2, (function (b, inputVar) {
}, empty, builder$1, (function (b, inputVar) {
return typeFilter(b, inputVar) + ("||" + inputVar + ".length!==" + length);
}), reverse);
}
Expand Down
Loading

0 comments on commit f4989bd

Please sign in to comment.