/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/ban-ts-comment */ import * as Dictionary from "@effect-ts/core/Collections/Immutable/Dictionary" import * as HashMap from "@effect-ts/core/Collections/Immutable/HashMap" import { pipe } from "@effect-ts/core/Function" import type { Compute, UnionToIntersection } from "@effect-ts/core/Utils" import { intersect } from "@effect-ts/core/Utils" import type * as fc from "fast-check" import * as S from "../custom.js" import type { Annotation } from "../custom/_schema/annotation.js" import { augmentRecord } from "../custom/_utils.js" import * as Arbitrary from "../custom/Arbitrary.js" import * as Encoder from "../custom/Encoder.js" import * as Guard from "../custom/Guard.js" import * as Parser from "../custom/Parser.js" import type { ParserEnv } from "../custom/Parser.js" import * as Th from "../custom/These.js" export class FromProperty< Self extends S.SchemaAny, Optional extends "optional" | "required", As extends Opt, Def extends Opt<["parser" | "constructor" | "both", () => S.ParsedShapeOf]> > { constructor( readonly _as: As, readonly _schema: Self, readonly _optional: Optional, readonly _def: Def, readonly _map: HashMap.HashMap, any> ) {} // Disabled because it sends the compiler down into rabbit holes.. // schema( // schema: That // ): FromProperty { // return new FromProperty(this._as, schema, this._optional, Opt.none, this._map) // } // opt(): FromProperty { // return new FromProperty(this._as, this._schema, "optional", this._def, this._map) // } // req(): FromProperty { // return new FromProperty(this._as, this._schema, "required", this._def, this._map) // } // from( // as: As1 // ): FromProperty, Def> { // return new FromProperty( // Opt(as), // this._schema, // this._optional, // this._def, // this._map // ) // } // removeFrom(): FromProperty { // return new FromProperty( // Opt.none, // this._schema, // this._optional, // this._def, // this._map // ) // } // def( // _: Optional extends "required" // ? () => S.ParsedShapeOf // : ["default can be set only for required properties", never] // ): FromProperty S.ParsedShapeOf]>> // def( // _: Optional extends "required" // ? () => S.ParsedShapeOf // : ["default can be set only for required properties", never], // k: K // ): FromProperty S.ParsedShapeOf]>> // def( // _: Optional extends "required" // ? () => S.ParsedShapeOf // : ["default can be set only for required properties", never], // k?: "parser" | "constructor" | "both" // ): FromProperty< // Self, // Optional, // As, // Some<["parser" | "constructor" | "both", () => S.ParsedShapeOf]> // > { // // @ts-expect-error // return new FromProperty( // this._as, // this._schema, // this._optional, // // @ts-expect-error // Opt([k ?? "both", _]), // this._map // ) // } // removeDef(): FromProperty { // return new FromProperty( // this._as, // this._schema, // this._optional, // Opt.none, // this._map // ) // } // getAnnotation(annotation: Annotation): Opt { // return HashMap.get_(this._map, annotation) // } // annotate( // annotation: Annotation, // value: A // ): FromProperty { // return new FromProperty( // this._as, // this._schema, // this._optional, // this._def, // HashMap.set_(this._map, annotation, value) // ) // } } export function fromPropFrom< Self extends S.SchemaAny, Optional extends "optional" | "required", As extends Opt, Def extends Opt<["parser" | "constructor" | "both", () => S.ParsedShapeOf]>, As1 extends PropertyKey >( prop: FromProperty, as: As1 ): FromProperty, Def> { return new FromProperty( Opt(as) as Some, prop._schema, prop._optional, prop._def, prop._map ) } export function fromProp( schema: Self ): FromProperty { return new FromProperty( Opt.none as None, schema, "required", Opt.none as None, HashMap.make() ) } export type AnyFromProperty = FromProperty export type FromPropertyRecord = Record export type ShapeFromFromProperties = Compute< UnionToIntersection< { [k in keyof Props]: Props[k] extends AnyFromProperty ? Props[k]["_optional"] extends "optional" ? { readonly [h in k]?: S.ParsedShapeOf } : { readonly [h in k]: S.ParsedShapeOf } : never }[keyof Props] >, "flat" > export type ConstructorFromFromProperties = Compute< UnionToIntersection< { [k in keyof Props]: k extends TagsFromFromProps ? never : Props[k] extends AnyFromProperty ? Props[k]["_optional"] extends "optional" ? { readonly [h in k]?: S.ParsedShapeOf } : Props[k]["_def"] extends Some<["constructor" | "both", any]> ? { readonly [h in k]?: S.ParsedShapeOf } : { readonly [h in k]: S.ParsedShapeOf } : never }[keyof Props] >, "flat" > export type EncodedFromFromProperties = Compute< UnionToIntersection< { [k in keyof Props]: Props[k] extends AnyFromProperty ? Props[k]["_optional"] extends "optional" ? { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]?: S.EncodedOf } : { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]: S.EncodedOf } : never }[keyof Props] >, "flat" > export type HasRequiredFromProperty = unknown extends { [k in keyof Props]: Props[k] extends AnyFromProperty ? Props[k]["_optional"] extends "required" ? unknown : never : never }[keyof Props] ? true : false // export type ParserErrorFromFromProperties = // S.CompositionE< // | S.PrevE> // | S.NextE< // HasRequiredFromProperty extends true // ? S.CompositionE< // | S.PrevE< // S.MissingKeysE< // { // [k in keyof Props]: Props[k] extends AnyFromProperty // ? Props[k]["_optional"] extends "optional" // ? never // : Props[k]["_def"] extends Some<["parser" | "both", any]> // ? never // : Props[k]["_as"] extends Some // ? Props[k]["_as"]["value"] // : k // : never // }[keyof Props] // > // > // | S.NextE< // S.StructE< // { // [k in keyof Props]: Props[k] extends AnyFromProperty // ? Props[k]["_optional"] extends "optional" // ? S.OptionalKeyE< // Props[k]["_as"] extends Some // ? Props[k]["_as"]["value"] // : k, // S.ParserErrorOf // > // : Props[k]["_def"] extends Some<["parser" | "both", any]> // ? S.OptionalKeyE< // Props[k]["_as"] extends Some // ? Props[k]["_as"]["value"] // : k, // S.ParserErrorOf // > // : S.RequiredKeyE< // Props[k]["_as"] extends Some // ? Props[k]["_as"]["value"] // : k, // S.ParserErrorOf // > // : never // }[keyof Props] // > // > // > // : S.StructE< // { // [k in keyof Props]: Props[k] extends AnyFromProperty // ? Props[k]["_optional"] extends "optional" // ? S.OptionalKeyE< // Props[k]["_as"] extends Some // ? Props[k]["_as"]["value"] // : k, // S.ParserErrorOf // > // : Props[k]["_def"] extends Some<["parser" | "both", any]> // ? S.OptionalKeyE< // Props[k]["_as"] extends Some // ? Props[k]["_as"]["value"] // : k, // S.ParserErrorOf // > // : S.RequiredKeyE< // Props[k]["_as"] extends Some // ? Props[k]["_as"]["value"] // : k, // S.ParserErrorOf // > // : never // }[keyof Props] // > // > // > export const fromPropertiesIdentifier = S.makeAnnotation<{ props: FromPropertyRecord }>() export type SchemaFromProperties = S.DefaultSchema< ParserInputFromFromProperties, ShapeFromFromProperties, ConstructorFromFromProperties, EncodedFromFromProperties, { props: Props } > export type TagsFromFromProps = { [k in keyof Props]: Props[k]["_as"] extends None ? Props[k]["_optional"] extends "required" ? S.ApiOf extends S.LiteralApi ? KS extends [string] ? k : never : never : never : never }[keyof Props] export function isFromPropertyRecord(u: unknown): u is FromPropertyRecord { return ( typeof u === "object" && u !== null && Object.keys(u).every(k => u[k] instanceof FromProperty) ) } export function tagsFromFromProps( props: Props ): Record { const keys = Object.keys(props) const tags = {} for (const key of keys) { const s: S.SchemaAny = props[key]._schema const def = props[key]._def as Opt< ["parser" | "constructor" | "both", () => S.ParsedShapeOf] > const as = props[key]._as as Opt if ( as.isNone() && def.isNone() && props[key]._optional === "required" && "literals" in s.Api && Array.isArray(s.Api["literals"]) && s.Api["literals"].length === 1 && typeof s.Api["literals"][0] === "string" ) { tags[key] = s.Api["literals"][0] } } return tags } export function fromProps( props: Props ): SchemaFromProperties { const parsers = {} as Record> const encoders = {} const guards = {} const arbitrariesReq = {} as Record> const arbitrariesPar = {} as Record> const keys = Object.keys(props) const required = [] as string[] const defaults = [] as [string, [string, any]][] for (const key of keys) { parsers[key] = Parser.for(props[key]._schema) encoders[key] = Encoder.for(props[key]._schema) guards[key] = Guard.for(props[key]._schema) if (props[key]._optional === "required") { const def = props[key]._def as Opt< ["parser" | "constructor" | "both", () => S.ParsedShapeOf] > const as = props[key]._as as Opt if (def.isNone() || (def.isSome() && def.value[0] === "constructor")) { required.push(as.getOrElse(() => key)) } if (def.isSome() && (def.value[0] === "constructor" || def.value[0] === "both")) { defaults.push([key, def.value]) } arbitrariesReq[key] = Arbitrary.for(props[key]._schema) } else { arbitrariesPar[key] = Arbitrary.for(props[key]._schema) } } const hasRequired = required.length > 0 function guard(_: unknown): _ is ShapeFromFromProperties { if (typeof _ !== "object" || _ === null) { return false } for (const key of keys) { const s = props[key] if (s._optional === "required" && !(key in _)) { return false } if (key in _) { if (!guards[key](_[key])) { return false } } } return true } function parser( _: unknown, env?: ParserEnv ): Th.These> { if (typeof _ !== "object" || _ === null) { return Th.fail( S.compositionE(Chunk(S.prevE(S.leafE(S.unknownRecordE(_))))) ) } let missingKeys = Chunk.empty() for (const k of required) { if (!(k in _)) { missingKeys = missingKeys.append(k) } } if (!missingKeys.isEmpty()) { return Th.fail( S.compositionE( Chunk( S.nextE(S.compositionE(Chunk(S.prevE(S.missingKeysE(missingKeys))))) ) ) ) } let errors = Chunk.empty< S.OptionalKeyE | S.RequiredKeyE >() let isError = false const result = {} const parsersv2 = env?.cache ? env.cache.getOrSetParsers(parsers) : parsers for (const key of keys) { const prop = props[key] const as = props[key]._as as Opt const _as: string = as.getOrElse(() => key) const def = prop._def as Opt< ["parser" | "constructor" | "both", () => S.ParsedShapeOf] > // TODO: support actual optionallity vs explicit `| undefined` if (_as in _) { const isUndefined = typeof _[_as] === "undefined" if (prop._optional === "optional" && isUndefined) { continue } if ( isUndefined && def.isSome() && // TODO: why def any // // @ts-expect-error (def.value[0] === "parser" || def.value[0] === "both") ) { // // @ts-expect-error result[key] = def.value[1]() continue } const res = parsersv2[key](_[_as]) if (res.effect._tag === "Left") { errors = errors.append( prop._optional === "required" ? S.requiredKeyE(_as, res.effect.left) : S.optionalKeyE(_as, res.effect.left) ) isError = true } else { result[key] = res.effect.right[0] const warnings = res.effect.right[1] if (warnings._tag === "Some") { errors = errors.append( prop._optional === "required" ? S.requiredKeyE(_as, warnings.value) : S.optionalKeyE(_as, warnings.value) ) } } } else { if ( def.isSome() && // // @ts-expect-error (def.value[0] === "parser" || def.value[0] === "both") ) { // // @ts-expect-error result[key] = def.value[1]() } } } if (!isError) { augmentRecord(result) } if (errors.isEmpty()) { return Th.succeed(result as ShapeFromFromProperties) } const error_ = S.compositionE(Chunk(S.nextE(S.structE(errors)))) const error = hasRequired ? S.compositionE(Chunk(S.nextE(error_))) : error_ if (isError) { return Th.fail(error) } // @ts-expect-error return Th.warn(result, error) } function encoder( _: ShapeFromFromProperties ): EncodedFromFromProperties { const enc = {} for (const key of keys) { if (key in _) { const as = props[key]._as as Opt const _as: string = as.getOrElse(() => key) enc[_as] = encoders[key](_[key]) } } // @ts-expect-error return enc } function arb(_: typeof fc): fc.Arbitrary> { const req = Dictionary.map_(arbitrariesReq, g => g(_)) const par = Dictionary.map_(arbitrariesPar, g => g(_)) // @ts-expect-error return _.record(req).chain(a => _.record(par, { withDeletedKeys: true }).map(b => intersect(a, b))) } const tags = tagsFromFromProps(props) return pipe( S.identity(guard), S.parser(parser), S.encoder(encoder), S.arbitrary(arb), S.constructor(_ => { const res = {} as ShapeFromFromProperties Object.assign(res, _, tags) for (const [k, v] of defaults) { if (!(k in res)) { if (v[0] === "constructor" || v[0] === "both") { res[k] = v[1]() } } } return Th.succeed(res) }), S.mapApi(() => ({ props })), S.withDefaults, S.annotate(fromPropertiesIdentifier, { props }) ) } export function fromPropsPick< Props extends FromPropertyRecord, KS extends (keyof Props)[] >(...ks: KS) { return ( self: Props ): Compute< UnionToIntersection< { [k in keyof Props]: k extends KS[number] ? { [h in k]: Props[h] } : never }[keyof Props] >, "flat" > => { const newProps = {} for (const k of Object.keys(self)) { if (!ks.includes(k)) { newProps[k] = self[k] } } // @ts-expect-error return newProps } } export function fromPropsOmit< Props extends FromPropertyRecord, KS extends (keyof Props)[] >(...ks: KS) { return ( self: Props ): Compute< UnionToIntersection< { [k in keyof Props]: k extends KS[number] ? never : { [h in k]: Props[h] } }[keyof Props] >, "flat" > => { const newProps = {} for (const k of Object.keys(self)) { if (ks.includes(k)) { newProps[k] = self[k] } } // @ts-expect-error return newProps } } export type ParserInputFromFromProperties = Compute< UnionToIntersection< { [k in keyof Props]: Props[k] extends AnyFromProperty ? Props[k]["_optional"] extends "optional" ? { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]?: S.ParsedShapeOf } : Props[k]["_def"] extends Some<["parser" | "both", any]> ? { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]?: S.ParsedShapeOf } : { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]: S.ParsedShapeOf } : never }[keyof Props] >, "flat" > type IsAnyOrUnknown = any extends T ? never : T type AorB = IsAnyOrUnknown extends never ? B : A export type ParserInputFromParserInputOrEncodedFromProperties< Props extends FromPropertyRecord > = Compute< UnionToIntersection< { [k in keyof Props]: Props[k] extends AnyFromProperty ? Props[k]["_optional"] extends "optional" ? { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]?: AorB< S.ParserInputOf, S.EncodedOf > } : Props[k]["_def"] extends Some<["parser" | "both", any]> ? { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]?: AorB< S.ParserInputOf, S.EncodedOf > } : { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]: AorB< S.ParserInputOf, S.EncodedOf > } : never }[keyof Props] >, "flat" > export type ParserInputFromParserInputOrEncodedFromSchema = T extends { Api: { props: infer Props } } ? Props extends FromPropertyRecord ? ParserInputFromParserInputOrEncodedFromProperties : never : never export type ParserInputFromEncodedFromProperties = Compute< UnionToIntersection< { [k in keyof Props]: Props[k] extends AnyFromProperty ? Props[k]["_optional"] extends "optional" ? { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]?: S.EncodedOf } : Props[k]["_def"] extends Some<["parser" | "both", any]> ? { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]?: S.EncodedOf } : { readonly [ h in Props[k]["_as"] extends Some ? Props[k]["_as"]["value"] : k ]: S.EncodedOf } : never }[keyof Props] >, "flat" > export type ParserInputFromEncodedFromSchema = T extends { Api: { props: infer Props } } ? Props extends FromPropertyRecord ? ParserInputFromEncodedFromProperties : never : never