import type { Types } from "effect" import { Predicate, Schema, Tuple } from "effect" import type * as FormField from "./FormField.js" type FormSchemaFields = Types.Simplify< { [key in keyof Fields]: Fields[key] extends Any ? FormSchema : Fields[key]["schema"] } > type FormSchemaStruct = T extends Schema.Struct.Fields ? Schema.Struct : never type FormSchema = FormSchemaStruct< FormSchemaFields > export const FormStructTypeId = Symbol.for("@inato/Form/FormBody/FormStruct") export type FormStructTypeId = typeof FormStructTypeId export const isFormStruct = ( value: unknown ): value is FormStruct => Predicate.hasProperty(value, FormStructTypeId) interface FormStruct { [FormStructTypeId]: FormStructTypeId fields: Fields schema: FormSchema defaultValue: FormSchema["Encoded"] } export const FormArrayTypeId = Symbol.for("@inato/Form/FormBody/FormArray") export type FormArrayTypeId = typeof FormArrayTypeId export const isFormArray = (value: unknown): value is AnyArray => Predicate.hasProperty(value, FormArrayTypeId) interface FormArray { [FormArrayTypeId]: FormArrayTypeId field: Field schema: Schema.Array$ defaultValue: FormArray["schema"]["Encoded"] } export const FormMapTypeId = Symbol.for("@inato/Form/FormBody/FormMap") export type FormMapTypeId = typeof FormMapTypeId export const isFormMap = (value: unknown): value is AnyMap => Predicate.hasProperty(value, FormMapTypeId) interface FormMap< Key extends Schema.Schema.AnyNoContext, Field extends AnyNonIterableField > { [FormMapTypeId]: FormMapTypeId field: Field keySchema: Key schema: Schema.HashMap defaultValue: FormMap["schema"]["Encoded"] defaultValueFor: ( keys: ReadonlyArray ) => FormMap["defaultValue"] } export const FormRawTypeId = Symbol.for("@inato/Form/FormBody/FormRaw") export type FormRawTypeId = typeof FormRawTypeId export const isFormRaw = (value: unknown): value is AnyRaw => Predicate.hasProperty(value, FormRawTypeId) interface FormRaw { [FormRawTypeId]: FormRawTypeId schema: S defaultValue: S["Encoded"] } const makeStructSchema = ( fields: Fields ): { schema: FormSchema defaultValue: FormSchema["Encoded"] } => { const schemaFields: Types.Mutable = {} const defaultValue: Record = {} for (const [key, field] of Object.entries(fields)) { schemaFields[key] = field.schema if ("matchDefaultValue" in field) { field.matchDefaultValue({ withDefaultValue: (value) => { defaultValue[key] = value } }) } else { defaultValue[key] = field.defaultValue } } // @ts-expect-error "structSchema is indeed of type FormSchema" const structSchema: FormSchema = Schema.Struct(schemaFields) return { schema: structSchema, defaultValue } } export const struct = ( fields: Fields ): FormStruct => { const { defaultValue, schema } = makeStructSchema(fields) return { [FormStructTypeId]: FormStructTypeId, fields, schema, defaultValue } } export const array = ( field: Field ): FormArray => { return { [FormArrayTypeId]: FormArrayTypeId, field, schema: Schema.Array(field.schema), defaultValue: [] } } export const map = ({ field, key }: { key: Schema.Schema field: Field }): FormMap, Field> => { return { [FormMapTypeId]: FormMapTypeId, field, keySchema: key, schema: Schema.HashMap({ key, value: field.schema }), defaultValue: [], defaultValueFor(keys) { const defaultValue: typeof field.schema.Encoded = "getDefaultValue" in field ? field.getDefaultValue() : field.defaultValue return keys.map((key) => Tuple.make(key, defaultValue)) } } } export const raw = ({ defaultValue, schema }: { schema: S defaultValue: S["Encoded"] }): FormRaw => { return { [FormRawTypeId]: FormRawTypeId, schema, defaultValue } } export type AnyNonIterableField = Any | FormField.Any export type AnyArray = FormArray export type AnyMap = FormMap export type AnyRaw = FormRaw export type AnyIterable = | FormArray | FormMap export type AnyField = AnyNonIterableField | AnyIterable | AnyRaw export type AnyFields = Record export type Any = FormStruct