Skip to content

Commit

Permalink
Add S.bigint
Browse files Browse the repository at this point in the history
  • Loading branch information
DZakh committed Sep 25, 2024
1 parent 2ea9c45 commit 78237da
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 37 deletions.
2 changes: 1 addition & 1 deletion IDEAS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ let trimContract: S.contract<string => string> = S.contract(s => {
- S.create / S.validate
- Rename S.inline to S.toRescriptCode
- Add serializeToJsonString to js api
- Add S.bigint
- Fix reverse for object/tuple/variant/recursive

### Done

- Add S.compile
- Add S.bigint

## v10

Expand Down
60 changes: 60 additions & 0 deletions packages/tests/src/core/S_bigint_test.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
open Ava

module Common = {
let value = 123n
let any = %raw(`123n`)
let invalidAny = %raw(`123.45`)
let factory = () => S.bigint

test("Successfully parses", t => {
let schema = factory()

t->Assert.deepEqual(any->S.parseAnyWith(schema), Ok(value), ())
})

test("Fails to parse", t => {
let schema = factory()

t->U.assertErrorResult(
invalidAny->S.parseAnyWith(schema),
{
code: InvalidType({expected: schema->S.toUnknown, received: invalidAny}),
operation: Parse,
path: S.Path.empty,
},
)
})

test("BigInt name", t => {
let schema = factory()
t->Assert.is(schema->S.name, "BigInt", ())
})

test("Successfully serializes", t => {
let schema = factory()

t->Assert.deepEqual(value->S.serializeToUnknownWith(schema), Ok(any), ())
})

test("Compiled parse code snapshot", t => {
let schema = factory()

t->U.assertCompiledCode(~schema, ~op=#Parse, `i=>{if(typeof i!=="bigint"){e[0](i)}return i}`)
})

test("Compiled serialize code snapshot", t => {
let schema = factory()

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

test("Reverse schema to self", t => {
let schema = factory()
t->Assert.is(schema->S.\"~experimantalReverse", schema->S.toUnknown, ())
})

test("Succesfully uses reversed schema for parsing back to initial value", t => {
let schema = factory()
t->U.assertReverseParsesBack(schema, value)
})
}
2 changes: 1 addition & 1 deletion packages/tests/src/core/S_float_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module Common = {
t->U.assertCompiledCodeIsNoop(~schema, ~op=#Serialize)
})

test("Reverse schema to self", t => {
test("Reverse schema to S.float", t => {
let schema = factory()
t->Assert.is(schema->S.\"~experimantalReverse", schema->S.toUnknown, ())
})
Expand Down
10 changes: 10 additions & 0 deletions packages/tests/src/core/S_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ test("Successfully parses float", (t) => {
expectType<TypeEqual<typeof value, number>>(true);
});

test("Successfully parses BigInt", (t) => {
const schema = S.bigint;
const value = schema.parseOrThrow(123n);

t.deepEqual(value, 123n);

expectType<TypeEqual<typeof schema, S.Schema<bigint, bigint>>>(true);
expectType<TypeEqual<typeof value, bigint>>(true);
});

test("Fails to parse float when NaN is provided", (t) => {
const schema = S.number;

Expand Down
3 changes: 3 additions & 0 deletions src/S.bs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ var $$int = S_Core$RescriptSchema.$$int;

var $$float = S_Core$RescriptSchema.$$float;

var bigint = S_Core$RescriptSchema.bigint;

var json = S_Core$RescriptSchema.json;

var literal = S_Core$RescriptSchema.literal;
Expand Down Expand Up @@ -190,6 +192,7 @@ export {
bool ,
$$int ,
$$float ,
bigint ,
json ,
literal ,
array ,
Expand Down
1 change: 1 addition & 0 deletions src/S.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const string: Schema<string>;
export const boolean: Schema<boolean>;
export const integer: Schema<number>;
export const number: Schema<number>;
export const bigint: Schema<bigint>;
export const never: Schema<never>;
export const unknown: Schema<unknown>;
export const undefined: Schema<undefined>;
Expand Down
1 change: 1 addition & 0 deletions src/S.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const string = S.string;
export const boolean = S.bool;
export const integer = S.$$int;
export const number = S.$$float;
export const bigint = S.bigint;
export const json = S.json;
export const never = S.never;
export const unknown = S.unknown;
Expand Down
1 change: 1 addition & 0 deletions src/S.resi
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ let string: t<string>
let bool: t<bool>
let int: t<int>
let float: t<float>
let bigint: t<bigint>

let json: (~validate: bool) => t<Js.Json.t>

Expand Down
44 changes: 28 additions & 16 deletions src/S_Core.bs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -944,18 +944,11 @@ function jsSerialize(value) {
}
}

function makeSchema(name, tagged, metadataMap, builder, maybeTypeFilter, reverse) {
function makeReverseSchema(name, tagged, metadataMap, builder, maybeTypeFilter) {
return {
t: tagged,
n: name,
r: (function () {
var original = this;
var reversed = reverse.call(original);
reversed.r = (function () {
return original;
});
return reversed;
}),
r: toSelf,
b: builder,
f: maybeTypeFilter,
i: 0,
Expand All @@ -971,11 +964,19 @@ function makeSchema(name, tagged, metadataMap, builder, maybeTypeFilter, reverse
};
}

function makeReverseSchema(name, tagged, metadataMap, builder, maybeTypeFilter) {
function makeSchema(name, tagged, metadataMap, builder, maybeTypeFilter, reverse) {
return {
t: tagged,
n: name,
r: toSelf,
r: (function () {
var original = this;
var reversed = reverse.call(original);
var reversed$1 = original !== reversed && typeof reversed.t === "string" ? makeReverseSchema(reversed.n, reversed.t, reversed.m, reversed.b, reversed.f) : reversed;
reversed$1.r = (function () {
return original;
});
return reversed$1;
}),
b: builder,
f: maybeTypeFilter,
i: 0,
Expand Down Expand Up @@ -1301,11 +1302,11 @@ function getWithDefault(schema, $$default) {
}));
}), schema.f, (function () {
var reversed = schema.r();
if (reversed.t.TAG !== "Option") {
if (reversed.t.TAG === "Option") {
return reversed.t._0;
} else {
return reversed;
}
var child = reversed.t._0;
return makeReverseSchema(child.n, child.t, child.m, child.b, child.f);
}));
}

Expand Down Expand Up @@ -1975,6 +1976,14 @@ function typeFilter$5(_b, inputVar) {

var schema$5 = makePrimitiveSchema("Float", noop, typeFilter$5);

function typeFilter$6(_b, inputVar) {
return "typeof " + inputVar + "!==\"bigint\"";
}

var schema$6 = makePrimitiveSchema("Unknown", noop, typeFilter$6);

schema$6.n = (() => "BigInt");

function parse$1(b, schemas, path, input, output) {
var isMultiple = schemas.length > 1;
var loop = function (idx, errorCodes) {
Expand Down Expand Up @@ -3077,6 +3086,8 @@ var $$int = schema$4;

var $$float = schema$5;

var bigint = schema$6;

var array = factory$2;

var dict = factory$4;
Expand All @@ -3097,7 +3108,7 @@ var parseAsyncWith = parseAnyAsyncWith;

var Schema = {};

var schema$6 = factory$7;
var schema$7 = factory$7;

var $$Object = {
factory: factory$3,
Expand Down Expand Up @@ -3159,6 +3170,7 @@ export {
bool ,
$$int ,
$$float ,
bigint ,
json ,
literal ,
array ,
Expand Down Expand Up @@ -3201,7 +3213,7 @@ export {
classify ,
setName ,
Schema ,
schema$6 as schema,
schema$7 as schema,
$$Object ,
object ,
Tuple ,
Expand Down
54 changes: 35 additions & 19 deletions src/S_Core.res
Original file line number Diff line number Diff line change
Expand Up @@ -1417,7 +1417,7 @@ let jsSerialize = value => {
}
}

let makeSchema = (~name, ~tagged, ~metadataMap, ~builder, ~maybeTypeFilter, ~reverse) => {
let makeReverseSchema = (~name, ~tagged, ~metadataMap, ~builder, ~maybeTypeFilter) => {
tagged,
builder,
isAsyncSchema: Unknown,
Expand All @@ -1432,15 +1432,10 @@ let makeSchema = (~name, ~tagged, ~metadataMap, ~builder, ~maybeTypeFilter, ~rev
jsParse,
jsParseAsync,
jsSerialize,
reverse: () => {
let original = %raw(`this`)
let reversed = (reverse->Obj.magic)["call"](original)
reversed.reverse = () => original
reversed
},
reverse: Reverse.toSelf,
}

let makeReverseSchema = (~name, ~tagged, ~metadataMap, ~builder, ~maybeTypeFilter) => {
let makeSchema = (~name, ~tagged, ~metadataMap, ~builder, ~maybeTypeFilter, ~reverse) => {
tagged,
builder,
isAsyncSchema: Unknown,
Expand All @@ -1455,7 +1450,25 @@ let makeReverseSchema = (~name, ~tagged, ~metadataMap, ~builder, ~maybeTypeFilte
jsParse,
jsParseAsync,
jsSerialize,
reverse: Reverse.toSelf,
reverse: () => {
let original = %raw(`this`)
let reversed = (reverse->Obj.magic)["call"](original)

// Copy primitive reversed schema to prevent mutating original reverse function
let reversed = if original !== reversed && reversed->classify->Js.typeof === "string" {
makeReverseSchema(
~name=reversed.name,
~tagged=reversed.tagged,
~metadataMap=reversed.metadataMap,
~builder=reversed.builder,
~maybeTypeFilter=reversed.maybeTypeFilter,
)
} else {
reversed
}
reversed.reverse = () => original
reversed
},
}

module Metadata = {
Expand Down Expand Up @@ -1950,16 +1963,7 @@ module Option = {
~reverse=() => {
let reversed = schema.reverse()
if reversed.tagged->unsafeGetVarianTag === "Option" {
let child = reversed.tagged->unsafeGetVariantPayload
// Copy to prevent mutating of primitive's reverse function
// TODO: Can be improved to copy only for primitives
makeReverseSchema(
~name=child.name,
~tagged=child.tagged,
~metadataMap=child.metadataMap,
~builder=child.builder,
~maybeTypeFilter=child.maybeTypeFilter,
)
reversed.tagged->unsafeGetVariantPayload
} else {
reversed
}
Expand Down Expand Up @@ -2964,6 +2968,17 @@ module Float = {
)
}

module BigInt = {
let typeFilter = (_b, ~inputVar) => `typeof ${inputVar}!=="bigint"`

let schema = makePrimitiveSchema(
~tagged=Unknown, // TODO: Add BigInt in v9
~builder=Builder.noop,
~maybeTypeFilter=Some(typeFilter),
)
(schema->Obj.magic)["n"] = %raw(`() => "BigInt"`)
}

module Union = {
let parse = (b, ~schemas, ~path, ~input, ~output) => {
let isMultiple = schemas->Js.Array2.length > 1
Expand Down Expand Up @@ -3677,6 +3692,7 @@ let string = String.schema
let bool = Bool.schema
let int = Int.schema
let float = Float.schema
let bigint = BigInt.schema
let null = Null.factory
let option = Option.factory
let array = Array.factory
Expand Down
1 change: 1 addition & 0 deletions src/S_Core.resi
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ let string: t<string>
let bool: t<bool>
let int: t<int>
let float: t<float>
let bigint: t<bigint>

let json: (~validate: bool) => t<Js.Json.t>

Expand Down

1 comment on commit 78237da

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 78237da Previous: 90ab99c Ratio
Parse string 811931571 ops/sec (±0.16%) 811431552 ops/sec (±0.15%) 1.00
Serialize string 811225754 ops/sec (±0.20%) 812781945 ops/sec (±0.05%) 1.00
Advanced object schema factory 464591 ops/sec (±1.11%) 472293 ops/sec (±0.54%) 1.02
Parse advanced object 56718411 ops/sec (±0.28%) 56799894 ops/sec (±0.57%) 1.00
Assert advanced object - compile 163982852 ops/sec (±0.21%)
Assert advanced object 172815185 ops/sec (±0.06%) 171920805 ops/sec (±0.20%) 0.99
Create and parse advanced object 94711 ops/sec (±0.30%) 95083 ops/sec (±0.21%) 1.00
Parse advanced strict object 25234682 ops/sec (±0.54%) 25471962 ops/sec (±0.17%) 1.01
Assert advanced strict object 29530322 ops/sec (±0.25%) 30460141 ops/sec (±0.20%) 1.03
Serialize advanced object 66936833 ops/sec (±3.42%) 74797850 ops/sec (±0.32%) 1.12

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.