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

Arbitrary: use Math.fround when creating number constraints, closes #484 #485

Merged
merged 1 commit into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
})
})
Loading