Skip to content

Commit

Permalink
Improve advanced reverse logic
Browse files Browse the repository at this point in the history
  • Loading branch information
DZakh committed Dec 5, 2024
1 parent 8580a4a commit d29acdc
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 133 deletions.
2 changes: 1 addition & 1 deletion IDEAS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ let trimContract: S.contract<string => string> = S.contract(s => {
- Changed asyncParser from (i) => () => promise to (i) => promise
- Look at the discriminant in unions - error message improvements
- Rename S.strict to S.strict (the same for strip)
- Added S.deepStrict

## v9.1

- Add s.strict s.strip to ppx
- Add S.deepStrict
- Add S.test

## v10
Expand Down
10 changes: 3 additions & 7 deletions packages/tests/src/core/S_object_flatten_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,8 @@ test("Flatten schema with duplicated field of the same type (flatten last)", t =
~op=#Parse,
`i=>{if(!i||i.constructor!==Object){e[1](i)}let v0=i["foo"];if(typeof v0!=="string"){e[0](v0)}return {"foo":v0,"bar":v0,}}`,
)
// FIXME: Should validate that the fields are equal
t->U.assertCompiledCode(
~schema,
~op=#ReverseConvert,
`i=>{return {"foo":i["foo"],"foo":i["bar"],}}`,
)
// FIXME: Should validate that the fields are equal and choose the right one depending on the order
t->U.assertCompiledCode(~schema, ~op=#ReverseConvert, `i=>{return {"foo":i["bar"],}}`)
})

test("Flatten schema with duplicated field of different type", t => {
Expand Down Expand Up @@ -254,7 +250,7 @@ test("Successfully serializes simple object with flatten", t => {
t->U.assertCompiledCode(
~op=#ReverseConvert,
~schema,
`i=>{return {"foo":i["foo"],"bar":i["bar"],}}`,
`i=>{return {"bar":i["bar"],"foo":i["foo"],}}`,
)
})

Expand Down
63 changes: 63 additions & 0 deletions packages/tests/src/core/S_object_nested_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,45 @@ test("S.schema object with a deep strict applied to the nested field parent", t
t->U.assertCompiledCode(~schema, ~op=#ReverseConvert, `i=>{let v0=i["nested"];return i}`)
})

test("S.schema object with a deep strict applied to the nested field parent + reverse", t => {
let schema =
S.schema(s =>
{
"nested": {
"foo": s.matches(S.null(S.string)),
},
}
)
->S.reverse
->S.deepStrict

t->U.unsafeAssertEqualSchemas(
schema,
S.object(s =>
s.field(
"nested",
S.schema(
s =>
{
"foo": s.matches(S.option(S.string)),
},
)->S.strict,
)
)->S.strict,
)

t->U.assertCompiledCode(
~schema,
~op=#Parse,
`i=>{if(!i||i.constructor!==Object){e[4](i)}let v0=i["nested"],v4;if(!v0||v0.constructor!==Object){e[0](v0)}let v1=v0["foo"],v2,v3;if(v1!==void 0&&(typeof v1!=="string")){e[1](v1)}if(v1!==void 0){v2=v1}else{v2=null}for(v3 in v0){if(v3!=="foo"){e[2](v3)}}for(v4 in i){if(v4!=="nested"){e[3](v4)}}return {"nested":{"foo":v2,},}}`,
)
t->U.assertCompiledCode(
~schema,
~op=#ReverseConvert,
`i=>{let v0=i["nested"],v1=v0["foo"],v2;if(v1!==null){v2=v1}else{v2=void 0}return {"nested":{"foo":v2,},}}`,
)
})

test("Object with a deep strict applied to the nested field parent", t => {
let schema = S.object(s => s.nested("nested").field("foo", S.string))->S.deepStrict

Expand All @@ -338,6 +377,30 @@ test("Object with a deep strict applied to the nested field parent", t => {
t->U.assertCompiledCode(~schema, ~op=#ReverseConvert, `i=>{return {"nested":{"foo":i,},}}`)
})

test("Object with a deep strict applied to the nested field parent + reverse", t => {
let schema =
S.object(s => {"foo": s.nested("nested").field("foo", S.string)})
->S.reverse
->S.deepStrict

t->U.unsafeAssertEqualSchemas(
schema,
S.schema(s =>
{
"foo": s.matches(S.string),
}
)->S.strict,
)

t->U.assertCompiledCode(
~schema,
~op=#Parse,
// FIXME: Missing type validation and strictness check
`i=>{if(!i||i.constructor!==Object){e[0](i)}return {"nested":{"foo":i["foo"],},}}`,
)
t->U.assertCompiledCode(~schema, ~op=#ReverseConvert, `i=>{return {"nested":{"foo":i["foo"],},}}`)
})

test("Object with nested field together with flatten", t => {
let schema = S.object(s =>
{
Expand Down
101 changes: 44 additions & 57 deletions src/S_Core.bs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1928,13 +1928,14 @@ function description(schema) {
return schema.m[descriptionMetadataId];
}

function getFullItemPath(item) {
switch (item.k) {
case 1 :
return getFullItemPath(item.of) + item.p;
function getFullDitemPath(ditem) {
switch (ditem.k) {
case 0 :
return "[" + ditem.inlinedLocation + "]";
case 1 :
return getFullDitemPath(ditem.of) + ditem.p;
case 2 :
return item.p;
return ditem.p;

}
}
Expand Down Expand Up @@ -2060,7 +2061,7 @@ function definitionToRitem(definition, path, ritems, ritemsByItemPath) {
s: ritem_2
};
item.r = ritem;
ritemsByItemPath[getFullItemPath(item)] = ritem;
ritemsByItemPath[getFullDitemPath(item)] = ritem;
return ritem;
}
if (Array.isArray(definition)) {
Expand Down Expand Up @@ -2286,8 +2287,9 @@ function nested(fieldName) {
return ctx$1;
}

function advancedReverse(definition, kind, ditems) {
function advancedReverse(definition, to, flattened) {
return function () {
var originalSchema = this;
var ritemsByItemPath = {};
var ritems = [];
var ritem = definitionToRitem(definition, "", ritems, ritemsByItemPath);
Expand Down Expand Up @@ -2381,7 +2383,7 @@ function advancedReverse(definition, kind, ditems) {

}
};
var getItemOutput = function (item) {
var getItemOutput = function (item, itemPath) {
var ritem = item.r;
if (ritem !== undefined) {
var reversed = ritem.s;
Expand All @@ -2395,38 +2397,32 @@ function advancedReverse(definition, kind, ditems) {
return reversed.b(b, itemInput, reversed, path$2);
}
var reversed$1 = item.schema["~r"]();
var input = reversedToInput(reversed$1, item.p);
var input = reversedToInput(reversed$1, itemPath);
var prevFlag = b.g.o;
b.g.o = (prevFlag | 1) ^ 1;
var output = reversed$1.b(b, input, reversed$1, path);
b.g.o = prevFlag;
return output;
};
var schemaOutput = function (ditems, isArray) {
var objectVal = make(b, isArray);
for(var idx = 0 ,idx_finish = ditems.length; idx < idx_finish; ++idx){
var item = ditems[idx];
switch (item.k) {
case 0 :
add(objectVal, item.inlinedLocation, getItemOutput(item));
break;
case 1 :
break;
case 2 :
merge(objectVal, getItemOutput(item));
break;

}
if (to !== undefined) {
return getItemOutput(to, "");
}
var isArray = originalSchema.t.TAG === "tuple";
var items = originalSchema.t.items;
var objectVal = make(b, isArray);
if (flattened !== undefined) {
for(var idx$1 = 0 ,idx_finish$1 = flattened.length; idx$1 < idx_finish$1; ++idx$1){
merge(objectVal, getItemOutput(flattened[idx$1], ""));
}
return complete(objectVal, isArray);
};
if (kind === "To") {
return getItemOutput(ditems[0]);
} else if (kind === "Array") {
return schemaOutput(ditems, true);
} else {
return schemaOutput(ditems, false);
}
for(var idx$2 = 0 ,idx_finish$2 = items.length; idx$2 < idx_finish$2; ++idx$2){
var item = items[idx$2];
if (!objectVal[item.inlinedLocation]) {
add(objectVal, item.inlinedLocation, getItemOutput(item, "[" + item.inlinedLocation + "]"));
}

}
return complete(objectVal, isArray);
});
return reversed;
};
Expand Down Expand Up @@ -2521,11 +2517,10 @@ function to(schema, definer) {
var output = definitionToOutput(bb, definition, getItemOutput);
b.c = b.c + allocateScope(bb);
return output;
}), schema.f, advancedReverse(definition, "To", [item]));
}), schema.f, advancedReverse(definition, item, undefined));
}

function object(definer) {
var ditems = [];
var flattened = (void 0);
var items = [];
var fields = {};
Expand All @@ -2546,9 +2541,10 @@ function object(definer) {

} else {
var item$1 = {
k: 0,
schema: flattenedSchema,
location: $$location,
inlinedLocation: inlinedLocation
inlinedLocation: inlinedLocation,
location: $$location
};
items.push(item$1);
fields[$$location] = item$1;
Expand All @@ -2563,7 +2559,6 @@ function object(definer) {
i: item_2
};
f.push(item$2);
ditems.push(item$2);
return proxify(item$2);
}
var message = "The '" + schema.n() + "' schema can't be flattened";
Expand All @@ -2574,17 +2569,14 @@ function object(definer) {
if (fields[fieldName]) {
throw new Error("[rescript-schema] " + ("The field " + inlinedLocation + " defined twice with incompatible schemas"));
}
var ditem_3 = "[" + inlinedLocation + "]";
var ditem = {
k: 0,
schema: schema,
inlinedLocation: inlinedLocation,
location: fieldName,
p: ditem_3
location: fieldName
};
fields[fieldName] = ditem;
items.push(ditem);
ditems.push(ditem);
return proxify(ditem);
};
var tag = function (tag$1, asValue) {
Expand All @@ -2611,7 +2603,7 @@ function object(definer) {
advanced: true
},
n: name$1,
"~r": advancedReverse(definition, "Object", ditems),
"~r": advancedReverse(definition, undefined, flattened),
b: advancedBuilder(definition, flattened),
f: typeFilter$1,
i: 0,
Expand All @@ -2620,22 +2612,20 @@ function object(definer) {
}

function tuple(definer) {
var ditems = [];
var items = [];
var item = function (idx, schema) {
var $$location = idx.toString();
var inlinedLocation = "\"" + $$location + "\"";
if (ditems[idx]) {
if (items[idx]) {
throw new Error("[rescript-schema] " + ("The item [" + inlinedLocation + "] is defined multiple times"));
}
var ditem_3 = "[" + inlinedLocation + "]";
var ditem = {
k: 0,
schema: schema,
inlinedLocation: inlinedLocation,
location: $$location,
p: ditem_3
location: $$location
};
ditems[idx] = ditem;
items[idx] = ditem;
return proxify(ditem);
};
var tag = function (idx, asValue) {
Expand All @@ -2646,26 +2636,23 @@ function tuple(definer) {
tag: tag
};
var definition = definer(ctx);
for(var idx = 0 ,idx_finish = ditems.length; idx < idx_finish; ++idx){
if (!ditems[idx]) {
for(var idx = 0 ,idx_finish = items.length; idx < idx_finish; ++idx){
if (!items[idx]) {
var $$location = idx.toString();
var inlinedLocation = "\"" + $$location + "\"";
var ditem_3 = "[" + inlinedLocation + "]";
var ditem = {
k: 0,
schema: unit,
inlinedLocation: inlinedLocation,
location: $$location,
p: ditem_3
inlinedLocation: inlinedLocation
};
ditems[idx] = ditem;
items[idx] = ditem;
}

}
return makeSchema(name$2, {
TAG: "tuple",
items: ditems
}, empty, advancedBuilder(definition, undefined), typeFilter$2, advancedReverse(definition, "Array", ditems));
items: items
}, empty, advancedBuilder(definition, undefined), typeFilter$2, advancedReverse(definition, undefined, undefined));
}

function definitionToSchema(definition) {
Expand Down
Loading

0 comments on commit d29acdc

Please sign in to comment.