import type { TypeLiteral } from "@fncts/schema/AST"; import type { Union } from "@fncts/typelevel"; import type { OptionalKeys, RequiredKeys } from "@fncts/typelevel/Object"; import { ownKeys } from "@fncts/schema/utils"; /** * @tsplus derive fncts.schema.Schema<_> 10 */ export function deriveValidation>( ...[base, brands]: Check> extends Check.True ? [ base: Schema>, brands: { [K in keyof A[Brand.valid] & string]: Validation; }, ] : never ): Schema { return Schema.fromAST( AST.createValidation( base.ast, Vector.from(Object.values(brands)), base.ast.annotations.annotate(ASTAnnotation.Brand, Vector.from(Object.values(brands))), ), ); } /** * @tsplus derive fncts.schema.Schema<_> 20 */ export function deriveLiteral( ...[value]: Check & Check.Not>> extends Check.True ? [value: A] : never ): Schema { return Schema.literal(value); } type MaybeKeys = { [K in keyof A]: A[K] extends Maybe ? K : never }[keyof A]; type IndexSignatures> = Union.ListOf< { [K in keyof A]: Check> extends Check.True ? never : { key: Schema; value: Schema }; }[keyof A] >; /** * @tsplus derive fncts.schema.Schema<_> 20 */ export function deriveStruct>( ...[requiredFields, optionalFields, maybeFields, indexSchema]: Check> extends Check.True ? [ ...[ requiredFields: { [k in Exclude, MaybeKeys>]: Schema; }, ], ...([OptionalKeys] extends [never] ? [optionalFields: {}] : [ optionalFields: { [k in OptionalKeys]: Schema; }, ]), ...([MaybeKeys] extends [never] ? [maybeFields: {}] : [ maybeFields: { [k in MaybeKeys]: [A[k]] extends [Maybe] ? Schema<_A> : never; }, ]), ...[indexSchema: IndexSignatures], ] : never ): Schema { const maybeFieldsKeys: Vector = ownKeys(maybeFields); let propertySignatures = ownKeys(requiredFields).map((key) => // @ts-expect-error AST.createPropertySignature(key, requiredFields[key]!.ast, false, true), ); const indexSignatures = (indexSchema as ReadonlyArray<{ key: Schema; value: Schema }>).map( ({ key, value }) => AST.createIndexSignature(key.ast as any, value.ast, false), ); if (optionalFields) { propertySignatures = propertySignatures.concat( // @ts-expect-error ownKeys(optionalFields).map((key) => AST.createPropertySignature(key, optionalFields[key]!.ast, true, true)), ); } const struct = Schema.fromAST(AST.createTypeLiteral(propertySignatures, Vector.from(indexSignatures))); if (maybeFieldsKeys.isEmpty()) { return struct as Schema; } propertySignatures = (struct.ast as TypeLiteral).propertySignatures; const from = Schema.fromAST( AST.createTypeLiteral( propertySignatures.concat( maybeFieldsKeys.map((key) => AST.createPropertySignature( key, // @ts-expect-error AST.createUnion(Vector(AST.undefinedKeyword, AST.createLiteral(null), maybeFields![key]!.ast)), true, true, ), ), ), Vector.from(indexSignatures), ), ); const to = Schema.fromAST( AST.createTypeLiteral( propertySignatures.concat( maybeFieldsKeys.map((key) => // @ts-expect-error AST.createPropertySignature(key, Schema.maybe(maybeFields![key]!).ast, false, true), ), ), Vector.from(indexSignatures), ), ); return from.transform( to, (input) => { const output = { ...input }; for (const key of maybeFieldsKeys) { output[key] = Maybe.fromNullable(input[key]); } return output; }, (input) => { const output = { ...input }; for (const key of maybeFieldsKeys) { const value: Maybe = input[key]; if (value.isNothing()) { delete output[key]; continue; } output[key] = value.value; } return output; }, ); } /** * @tsplus derive fncts.schema.Schema<_> 10 */ export function deriveTuple>( ...[components]: Check> extends Check.True ? [components: { [K in keyof A]: Schema }] : never ): Schema { return unsafeCoerce(Schema.tuple(...components)); } /** * @tsplus derive fncts.schema.Schema lazy */ export function deriveLazy(f: (_: Schema) => Schema): Schema { let cached: AST | undefined; const ast = AST.createLazy(() => { if (!cached) { cached = f(Schema.fromAST(ast)).ast; } return cached; }); return Schema.fromAST(ast); } /** * @tsplus derive fncts.schema.Schema[fncts.ReadonlyArray]<_> 10 */ export function deriveReadonlyArray>( ...[element]: [A] extends [ReadonlyArray] ? Check>> extends Check.True ? [element: Schema<_A>] : never : never ): Schema { return Schema.array(element) as unknown as Schema; } /** * @tsplus derive fncts.schema.Schema[fncts.Array]<_> 10 */ export function deriveArray>( ...[element]: [A] extends [Array] ? Check>> extends Check.True ? [element: Schema<_A>] : never : never ): Schema { return Schema.mutableArray(element) as unknown as Schema; } /** * @tsplus derive fncts.schema.Schema<_> 15 */ export function deriveRecord>( ...[keySchema, valueSchema]: [A] extends [Record] ? Check> & Check.IsEqual>> extends Check.True ? [keySchema: Schema, valueSchema: Schema] : never : never ): Schema { return Schema.record(keySchema as Schema, valueSchema) as Schema; }