Skip to content

Commit

Permalink
refactor annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
gcanti committed Oct 14, 2024
1 parent 418ae94 commit 757d86a
Show file tree
Hide file tree
Showing 16 changed files with 463 additions and 489 deletions.
2 changes: 1 addition & 1 deletion packages/effect/dtslint/SchemaClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as S from "effect/Schema"
// ---------------------------------------------

type HasFields<Fields extends S.Struct.Fields> = S.Struct<Fields> | {
readonly [S.refineTypeId]: HasFields<Fields>
readonly [S.refineSchemaId]: HasFields<Fields>
}

declare const checkForConflicts: <Fields extends S.Struct.Fields>(
Expand Down
77 changes: 33 additions & 44 deletions packages/effect/src/Arbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,41 +21,30 @@ export interface LazyArbitrary<A> {
}

/**
* @category hooks
* @since 3.10.0
*/
export const ArbitraryHookId: unique symbol = Symbol.for("effect/Schema/ArbitraryHookId")

/**
* @category hooks
* @since 3.10.0
*/
export type ArbitraryHookId = typeof ArbitraryHookId

/**
* @category hooks
* @category annotations
* @since 3.10.0
*/
export interface GenerationContext {
export interface ArbitraryGenerationContext {
readonly depthIdentifier?: string
readonly maxDepth: number
}

/**
* @category hooks
* @category annotations
* @since 3.10.0
*/
export type ArbitraryAnnotation<A> = (
...args: [...ReadonlyArray<LazyArbitrary<any>>, GenerationContext]
export type ArbitraryAnnotation<A, TypeParameters extends ReadonlyArray<any> = readonly []> = (
...arbitraries: [
...{ readonly [K in keyof TypeParameters]: LazyArbitrary<TypeParameters[K]> },
ctx: ArbitraryGenerationContext
]
) => LazyArbitrary<A>

/**
* @category annotations
* @since 3.10.0
*/
export const arbitrary =
<A>(annotation: ArbitraryAnnotation<A>) => <I, R>(self: Schema.Schema<A, I, R>): Schema.Schema<A, I, R> =>
self.annotations({ [ArbitraryHookId]: annotation })
export const ArbitraryAnnotationId = Symbol.for("effect/annotation/Arbitrary")

/**
* Returns a LazyArbitrary for the `A` type of the provided schema.
Expand All @@ -74,7 +63,7 @@ export const makeLazy = <A, I, R>(schema: Schema.Schema<A, I, R>): LazyArbitrary
*/
export const make = <A, I, R>(schema: Schema.Schema<A, I, R>): FastCheck.Arbitrary<A> => makeLazy(schema)(FastCheck)

const getHook = AST.getAnnotation<ArbitraryAnnotation<any>>(ArbitraryHookId)
const getAnnotation = AST.getAnnotation<ArbitraryAnnotation<any, any>>(ArbitraryAnnotationId)

const getRefinementFromArbitrary = (
ast: AST.Refinement,
Expand Down Expand Up @@ -121,7 +110,7 @@ const getSuspendedArray = (
)
}

interface Context extends GenerationContext {
interface Context extends ArbitraryGenerationContext {
readonly constraints?: Constraints
}

Expand All @@ -130,15 +119,15 @@ const go = (
ctx: Context,
path: ReadonlyArray<PropertyKey>
): LazyArbitrary<any> => {
const hook = getHook(ast)
if (Option.isSome(hook)) {
const annotation = getAnnotation(ast)
if (Option.isSome(annotation)) {
switch (ast._tag) {
case "Declaration":
return hook.value(...ast.typeParameters.map((p) => go(p, ctx, path)), ctx)
return annotation.value(...ast.typeParameters.map((p) => go(p, ctx, path)), ctx)
case "Refinement":
return hook.value(getRefinementFromArbitrary(ast, ctx, path), ctx)
return annotation.value(getRefinementFromArbitrary(ast, ctx, path), ctx)
default:
return hook.value(ctx)
return annotation.value(ctx)
}
}
switch (ast._tag) {
Expand Down Expand Up @@ -446,36 +435,36 @@ export const getConstraints = (ast: AST.Refinement): Constraints | undefined =>
const jsonSchema: any = ast.annotations[AST.JSONSchemaAnnotationId]
switch (TypeAnnotationId) {
// int
case filters_.IntTypeId:
case filters_.IntSchemaId:
return new IntegerConstraints({})
// number
case filters_.GreaterThanTypeId:
case filters_.GreaterThanOrEqualToTypeId:
case filters_.LessThanTypeId:
case filters_.LessThanOrEqualToTypeId:
case filters_.BetweenTypeId:
case filters_.GreaterThanSchemaId:
case filters_.GreaterThanOrEqualToSchemaId:
case filters_.LessThanSchemaId:
case filters_.LessThanOrEqualToSchemaId:
case filters_.BetweenSchemaId:
return new NumberConstraints({
min: jsonSchema.exclusiveMinimum ?? jsonSchema.minimum,
max: jsonSchema.exclusiveMaximum ?? jsonSchema.maximum
})
// bigint
case filters_.GreaterThanBigintTypeId:
case filters_.GreaterThanOrEqualToBigIntTypeId:
case filters_.LessThanBigIntTypeId:
case filters_.LessThanOrEqualToBigIntTypeId:
case filters_.BetweenBigintTypeId: {
case filters_.GreaterThanBigintSchemaId:
case filters_.GreaterThanOrEqualToBigIntSchemaId:
case filters_.LessThanBigIntSchemaId:
case filters_.LessThanOrEqualToBigIntSchemaId:
case filters_.BetweenBigintSchemaId: {
const constraints: any = ast.annotations[TypeAnnotationId]
return new BigIntConstraints(constraints)
}
// string
case filters_.MinLengthTypeId:
case filters_.MaxLengthTypeId:
case filters_.LengthTypeId:
case filters_.MinLengthSchemaId:
case filters_.MaxLengthSchemaId:
case filters_.LengthSchemaId:
return new StringConstraints(jsonSchema)
// array
case filters_.MinItemsTypeId:
case filters_.MaxItemsTypeId:
case filters_.ItemsCountTypeId:
case filters_.MinItemsSchemaId:
case filters_.MaxItemsSchemaId:
case filters_.ItemsCountSchemaId:
return new ArrayConstraints({
minLength: jsonSchema.minItems,
maxLength: jsonSchema.maxItems
Expand Down
3 changes: 1 addition & 2 deletions packages/effect/src/JSONSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*/

import * as errors_ from "./internal/schema/errors.js"
import * as filters_ from "./internal/schema/filters.js"
import * as Option from "./Option.js"
import * as Predicate from "./Predicate.js"
import * as Record from "./Record.js"
Expand Down Expand Up @@ -319,7 +318,7 @@ const getRefinementInnerTransformation = (ast: AST.Refinement): AST.AST | undefi
}

const isParseJsonTransformation = (ast: AST.AST): boolean =>
ast.annotations[AST.TypeAnnotationId] === filters_.ParseJsonTypeId
ast.annotations[AST.TypeAnnotationId] === AST.ParseJsonSchemaId

function merge(a: JsonSchemaAnnotations, b: JsonSchema7): JsonSchema7
function merge(a: JsonSchema7, b: JsonSchemaAnnotations): JsonSchema7
Expand Down
40 changes: 16 additions & 24 deletions packages/effect/src/Pretty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,29 @@ export interface Pretty<To> {
}

/**
* @category hooks
* @since 3.10.0
*/
export const PrettyHookId: unique symbol = Symbol.for("effect/Schema/PrettyHookId")

/**
* @category hooks
* @category annotations
* @since 3.10.0
*/
export type PrettyHookId = typeof PrettyHookId
export type PrettyAnnotation<A, TypeParameters extends ReadonlyArray<any> = readonly []> = (
...pretties: { readonly [K in keyof TypeParameters]: Pretty<TypeParameters[K]> }
) => Pretty<A>

/**
* @category annotations
* @since 3.10.0
*/
export const pretty =
<A>(handler: (...args: ReadonlyArray<Pretty<any>>) => Pretty<A>) =>
<I, R>(self: Schema.Schema<A, I, R>): Schema.Schema<A, I, R> => self.annotations({ [PrettyHookId]: handler })
export const PrettyAnnotationId = Symbol.for("effect/annotation/Pretty")

/**
* @category prettify
* @since 3.10.0
*/
export const make = <A, I, R>(schema: Schema.Schema<A, I, R>): (a: A) => string => compile(schema.ast, [])

const getHook = AST.getAnnotation<(...args: ReadonlyArray<Pretty<any>>) => Pretty<any>>(
PrettyHookId
)
const getAnnotation = AST.getAnnotation<PrettyAnnotation<any, any>>(PrettyAnnotationId)

const getMatcher = (defaultPretty: Pretty<any>) => (ast: AST.AST): Pretty<any> =>
Option.match(getHook(ast), {
Option.match(getAnnotation(ast), {
onNone: () => defaultPretty,
onSome: (handler) => handler()
})
Expand All @@ -64,9 +56,9 @@ const formatUnknown = getMatcher(util_.formatUnknown)
*/
export const match: AST.Match<Pretty<any>> = {
"Declaration": (ast, go, path) => {
const hook = getHook(ast)
if (Option.isSome(hook)) {
return hook.value(...ast.typeParameters.map((tp) => go(tp, path)))
const annotation = getAnnotation(ast)
if (Option.isSome(annotation)) {
return annotation.value(...ast.typeParameters.map((tp) => go(tp, path)))
}
throw new Error(errors_.getPrettyMissingAnnotationErrorMessage(path, ast))
},
Expand All @@ -92,7 +84,7 @@ export const match: AST.Match<Pretty<any>> = {
"BigIntKeyword": getMatcher((a) => `${String(a)}n`),
"Enums": stringify,
"TupleType": (ast, go, path) => {
const hook = getHook(ast)
const hook = getAnnotation(ast)
if (Option.isSome(hook)) {
return hook.value()
}
Expand Down Expand Up @@ -134,7 +126,7 @@ export const match: AST.Match<Pretty<any>> = {
}
},
"TypeLiteral": (ast, go, path) => {
const hook = getHook(ast)
const hook = getAnnotation(ast)
if (Option.isSome(hook)) {
return hook.value()
}
Expand Down Expand Up @@ -179,7 +171,7 @@ export const match: AST.Match<Pretty<any>> = {
}
},
"Union": (ast, go, path) => {
const hook = getHook(ast)
const hook = getAnnotation(ast)
if (Option.isSome(hook)) {
return hook.value()
}
Expand All @@ -193,7 +185,7 @@ export const match: AST.Match<Pretty<any>> = {
}
},
"Suspend": (ast, go, path) => {
return Option.match(getHook(ast), {
return Option.match(getAnnotation(ast), {
onNone: () => {
const get = util_.memoizeThunk(() => go(ast.f(), path))
return (a) => get()(a)
Expand All @@ -202,13 +194,13 @@ export const match: AST.Match<Pretty<any>> = {
})
},
"Refinement": (ast, go, path) => {
return Option.match(getHook(ast), {
return Option.match(getAnnotation(ast), {
onNone: () => go(ast.from, path),
onSome: (handler) => handler()
})
},
"Transformation": (ast, go, path) => {
return Option.match(getHook(ast), {
return Option.match(getAnnotation(ast), {
onNone: () => go(ast.to, path),
onSome: (handler) => handler()
})
Expand Down
Loading

0 comments on commit 757d86a

Please sign in to comment.