import type { Tree } from "./tree.ts" import type { TreeNodePathName, TreePathName } from "./tree-path-name.ts" import type { Join, Split } from "./utility-types.ts" /** * For a `Tree` `T`, a record from every {@link TreePathName} in `T` (joined together with separator `J`) to a value of type `D`. */ export type TreeMap = { [K in Join, J>]: D } /** * Make a record from each {@link TreePathName} in `T` (joined together with separator `J`) the same ${TreePathName} in array form. * * @param tree `T`, the source of truth for determining valid paths * @param separator (default `"/"`) the separator to use when joining each {@link TreePathName} * @returns a {@link TreeMap} of `T` */ export function flattenTree( tree: T, separator = `/` as J, ): { [K in Join, J>]: Split } { const treePathNames = {} as { [K in Join, J>]: Split } const discoveredBranches = discoverBranches(``, [], [], tree, separator) for (const [pathName, path] of discoveredBranches) { treePathNames[pathName as Join, J>] = path as any } return treePathNames } /** * @internal */ export function discoverBranches( basePrefix: string, basePath: string[], discoveredBranches: [pathName: string, path: string[]][], tree: Tree, separator = `/` as J, ): [pathName: string, path: string[]][] { const [status, branches] = tree if (status === `optional`) { discoveredBranches.push([basePrefix, basePath]) } for (const [segment, maybeTree] of Object.entries(branches)) { const newPrefix = basePrefix ? `${basePrefix}${separator}${segment}` : segment const newBasePath = [...basePath, segment] if (maybeTree === null || maybeTree[0] === `optional`) { discoveredBranches.push([newPrefix, newBasePath]) } if (maybeTree) { discoverBranches( newPrefix, newBasePath, discoveredBranches, maybeTree, separator, ) } } return discoveredBranches } /** * Make a {@link TreeMap} from each {@link TreePathName} in `T` (joined together with separator `J`) to some data of type `D`. * * @param tree `T`, the source of truth for determining valid paths * @param mapper a function that maps each {@link TreePathName} in `T` to a value of type `D` * @param separator (default `"/"`) the separator to use when joining each {@link TreePathName} * @returns a {@link TreeMap} of `T` */ export function mapTree( tree: T, mapper: ( path: TreePathName, joinedPath: Join, J>, tree: T, ) => D, separator = `/` as J, ): TreeMap { const treeMap = {} as TreeMap const discoveredBranches = discoverBranches(``, [], [], tree, separator) for (const [pathName, path] of discoveredBranches) { treeMap[pathName as Join, J>] = mapper( path as TreePathName, pathName as Join, J>, tree, ) } return treeMap } export type TreeMapExhaustive = { [K in Join, J>]: P }