forked from botpress/botpress
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcommand-tree.ts
78 lines (63 loc) · 2.5 KB
/
command-tree.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import type * as typings from './typings'
export type DefinitionSubTree = {
description: string
subcommands: DefinitionTree
}
export type DefinitionTree = {
[key: string]: DefinitionTreeNode
}
export type DefinitionTreeNode = typings.CommandDefinition | DefinitionSubTree
export type ImplementationSubTree<D extends DefinitionSubTree = DefinitionSubTree> = {
subcommands: ImplementationTree<D['subcommands']>
}
export type ImplementationTreeNode<N extends DefinitionTreeNode = DefinitionTreeNode> =
N extends typings.CommandDefinition
? typings.CommandImplementation<N>
: N extends DefinitionSubTree
? ImplementationSubTree<N>
: never
export type ImplementationTree<D extends DefinitionTree = DefinitionTree> = {
[K in keyof D]: ImplementationTreeNode<D[K]>
}
export type CommandSubTree<D extends DefinitionSubTree = DefinitionSubTree> = {
description: string
subcommands: CommandTree<D['subcommands']>
}
export type CommandTreeNode<N extends DefinitionTreeNode = DefinitionTreeNode> = N extends typings.CommandDefinition
? typings.CommandLeaf<N>
: N extends DefinitionSubTree
? CommandSubTree<N>
: never
export type CommandTree<D extends DefinitionTree = DefinitionTree> = {
[K in keyof D]: CommandTreeNode<D[K]>
}
export const guards = {
definition: {
isDef: (x: DefinitionTreeNode): x is typings.CommandDefinition => 'schema' in x,
isSubTree: (x: DefinitionTreeNode): x is DefinitionSubTree => 'subcommands' in x,
},
implementation: {
isImpl: (x: ImplementationTreeNode): x is typings.CommandImplementation => typeof x === 'function',
isSubTree: (x: ImplementationTreeNode): x is ImplementationSubTree => typeof x === 'object',
},
command: {
isLeaf: (x: CommandTreeNode): x is typings.CommandLeaf => 'handler' in x,
isSubTree: (x: CommandTreeNode): x is CommandSubTree => 'subcommands' in x,
},
}
export const zipTree = <T extends DefinitionTree>(defTree: T, implTree: ImplementationTree<T>): CommandTree<T> => {
const tree = {} as CommandTree<T>
for (const key in defTree) {
const def = defTree[key]!
const impl = implTree[key]!
if (guards.definition.isDef(def) && guards.implementation.isImpl(impl)) {
tree[key] = { ...def, handler: impl } as CommandTreeNode<typeof def>
continue
}
if (guards.definition.isSubTree(def) && guards.implementation.isSubTree(impl)) {
tree[key] = { ...def, subcommands: zipTree(def.subcommands, impl.subcommands) } as CommandTreeNode<typeof def>
continue
}
}
return tree
}