import type * as T from '@traversable/registry' import { fn, isValidIdentifier, parseKey } from '@traversable/registry' import { t } from '@traversable/schema' import type { Index } from './functor.js' export type IR = | t.Leaf | t.eq | t.ref | t.array | t.record | t.optional | t.union | t.intersect | t.tuple | t.object<[k: string, T][]> export namespace IR { export type Options = { preSortIndex?: number } export const defaults = {} satisfies Options export function clone(schema: T, { preSortIndex }: Options = defaults) { return { ...schema, ...typeof preSortIndex === 'number' && { preSortIndex } } } } export type F = | t.Leaf | t.eq | t.array | t.record | t.optional | t.union | t.intersect | t.tuple | t.object<[k: string, v: T][]> export type Fixpoint = | t.Leaf | t.eq | t.array | t.record | t.optional | t.union | t.intersect | t.tuple | t.object<[k: string, v: Fixpoint][]> export interface Free extends T.HKT { [-1]: F } export type Context = { VAR: string indent(numberOfSpaces: number): string dedent(numberOfSpaces: number): string join(numberOfSpaces: number): string } export const makeIndent : (offset: number) => (numberOfSpaces: number) => string = (off) => (n) => `\r${' '.repeat(Math.max(off + n, 0))}` export const makeDedent : (offset: number) => (numberOfSpaces: number) => string = (off) => makeIndent(-off) export const makeJoin : (offset: number) => (numberOfSpaces: number) => string = (off) => fn.flow(makeIndent(off), (_) => `${_}&& `) export const buildContext : (ix: T.Require) => Context = ({ offset, varName: VAR }) => ({ VAR, indent: makeIndent(offset), dedent: makeDedent(offset), join: makeJoin(offset), }) export function keyAccessor(key: keyof any | undefined, $: Index) { return typeof key === 'string' ? isValidIdentifier(key) ? $.isOptional ? `?.${key}` : `.${key}` : `[${parseKey(key)}]` : '' } /** * Binding the element's index to the element itself is a hack to make sure * we preserve the original order of the tuple, even while sorting */ export const bindPreSortIndices: (x: T[]) => T[] = (x) => { for (let ix = 0, len = x.length; ix < len; ix++) { x[ix] = IR.clone(x[ix], { preSortIndex: ix }) } return x } /** * Reading `x` to access the "preSortIndex" is a hack to make sure * we preserve the original order of the tuple, even while sorting */ export function indexAccessor(index: keyof any | undefined, $: { isOptional?: boolean }, x?: any) { if ('preSortIndex' in x) { return $.isOptional ? `?.[${x.preSortIndex}]` : `[${x.preSortIndex}]` } else if (typeof index === 'number') { return $.isOptional ? `?.[${index}]` : `[${index}]` } else return '' }