Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

Commit

Permalink
Arbitrary: use Math.fround when creating number constraints, closes #484
Browse files Browse the repository at this point in the history
 (#485)
  • Loading branch information
gcanti authored Oct 15, 2023
1 parent 8a56b91 commit 0a20788
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .changeset/small-days-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/schema": patch
---

Arbitrary: use Math.fround when creating number constraints, closes #484
53 changes: 31 additions & 22 deletions src/Arbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,16 +241,34 @@ interface NumberConstraints {
readonly constraints: FastCheck.FloatConstraints
}

const numberConstraints = (constraints: NumberConstraints["constraints"]): NumberConstraints => {
if (Predicate.isNumber(constraints.min)) {
constraints.min = Math.fround(constraints.min)
}
if (Predicate.isNumber(constraints.max)) {
constraints.max = Math.fround(constraints.max)
}
return { _tag: "NumberConstraints", constraints }
}

interface StringConstraints {
readonly _tag: "StringConstraints"
readonly constraints: FastCheck.StringSharedConstraints
}

const stringConstraints = (constraints: StringConstraints["constraints"]): StringConstraints => {
return { _tag: "StringConstraints", constraints }
}

interface IntegerConstraints {
readonly _tag: "IntegerConstraints"
readonly constraints: FastCheck.IntegerConstraints
}

const integerConstraints = (constraints: IntegerConstraints["constraints"]): IntegerConstraints => {
return { _tag: "IntegerConstraints", constraints }
}

type Constraints = NumberConstraints | StringConstraints | IntegerConstraints

/** @internal */
Expand All @@ -260,18 +278,12 @@ export const getConstraints = (ast: AST.Refinement): Constraints | undefined =>
switch (TypeAnnotationId) {
case Schema.GreaterThanTypeId:
case Schema.GreaterThanOrEqualToTypeId:
return {
_tag: "NumberConstraints",
constraints: { min: jsonSchema.exclusiveMinimum ?? jsonSchema.minimum }
}
return numberConstraints({ min: jsonSchema.exclusiveMinimum ?? jsonSchema.minimum })
case Schema.LessThanTypeId:
case Schema.LessThanOrEqualToTypeId:
return {
_tag: "NumberConstraints",
constraints: { max: jsonSchema.exclusiveMaximum ?? jsonSchema.maximum }
}
return numberConstraints({ max: jsonSchema.exclusiveMaximum ?? jsonSchema.maximum })
case Schema.IntTypeId:
return { _tag: "IntegerConstraints", constraints: {} }
return integerConstraints({})
case Schema.BetweenTypeId: {
const min = jsonSchema.minimum
const max = jsonSchema.maximum
Expand All @@ -282,12 +294,12 @@ export const getConstraints = (ast: AST.Refinement): Constraints | undefined =>
if (Predicate.isNumber(max)) {
constraints.max = max
}
return { _tag: "NumberConstraints", constraints }
return numberConstraints(constraints)
}
case Schema.MinLengthTypeId:
return { _tag: "StringConstraints", constraints: { minLength: jsonSchema.minLength } }
return stringConstraints({ minLength: jsonSchema.minLength })
case Schema.MaxLengthTypeId:
return { _tag: "StringConstraints", constraints: { maxLength: jsonSchema.maxLength } }
return stringConstraints({ maxLength: jsonSchema.maxLength })
}
}

Expand All @@ -306,19 +318,19 @@ export const combineConstraints = (
case "NumberConstraints": {
switch (c2._tag) {
case "NumberConstraints": {
const out: NumberConstraints = {
_tag: "NumberConstraints",
constraints: { ...c1.constraints, ...c2.constraints }
const constraints: NumberConstraints["constraints"] = {
...c1.constraints,
...c2.constraints
}
const min = getMax(c1.constraints.min, c2.constraints.min)
if (Predicate.isNumber(min)) {
out.constraints.min = min
constraints.min = min
}
const max = getMin(c1.constraints.max, c2.constraints.max)
if (Predicate.isNumber(max)) {
out.constraints.max = max
constraints.max = max
}
return out
return numberConstraints(constraints)
}
case "IntegerConstraints": {
const out: IntegerConstraints = { ...c2 }
Expand All @@ -338,10 +350,7 @@ export const combineConstraints = (
case "StringConstraints": {
switch (c2._tag) {
case "StringConstraints": {
const out: StringConstraints = {
_tag: "StringConstraints",
constraints: { ...c1.constraints, ...c2.constraints }
}
const out: StringConstraints = stringConstraints({ ...c1.constraints, ...c2.constraints })
const min = getMax(c1.constraints.minLength, c2.constraints.minLength)
if (Predicate.isNumber(min)) {
out.constraints.minLength = min
Expand Down
5 changes: 5 additions & 0 deletions test/Arbitrary/Arbitrary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,4 +371,9 @@ describe("Arbitrary/Arbitrary", () => {
const schema = S.string.pipe(S.pattern(regexp))
propertyTo(schema)
})

it("should support doubles as constraints", () => {
const schema = S.number.pipe(S.clamp(1.3, 3.1))
propertyTo(schema)
})
})
9 changes: 8 additions & 1 deletion test/number/clamp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ import * as S from "@effect/schema/Schema"
import * as Util from "@effect/schema/test/util"

describe("number/clamp", () => {
const schema = S.number.pipe(S.clamp(-1, 1))
it("decoding", async () => {
const schema = S.number.pipe(S.clamp(-1, 1))
await Util.expectParseSuccess(schema, 3, 1)
await Util.expectParseSuccess(schema, 0, 0)
await Util.expectParseSuccess(schema, -3, -1)
})

it("should support doubles as constraints", async () => {
const schema = S.number.pipe(S.clamp(1.3, 3.1))
await Util.expectParseSuccess(schema, 4, 3.1)
await Util.expectParseSuccess(schema, 2, 2)
await Util.expectParseSuccess(schema, 1, 1.3)
})
})

0 comments on commit 0a20788

Please sign in to comment.