-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmachine.ts
128 lines (101 loc) · 3.01 KB
/
machine.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// eslint-disable max-len
import { Key_Base } from './schema'
import { States_Map_Base } from './schema'
import { Paths_Map_Base } from './schema'
import { States } from './schema'
import { Paths } from './schema'
import { Schema } from './schema'
import { States_Keys } from './schema'
import { States_Data } from './schema'
import { States_Values } from './schema'
import { Paths_Data } from './schema'
import { Schema_States } from './schema'
import { Schema_Paths } from './schema'
import { Schema_Enter } from './schema'
export type Machine_Schema <M extends Machine<any>> = M extends Machine<infer Schema> ? Schema : never
export type Machine_Narrow
<
M extends Machine<any>,
Key extends States_Keys<Schema_States<Machine_Schema<M>>>,
>
=
M extends Machine<Schema<States<infer S>, any>>
?
Machine_Concrete<Key, ReturnType<S[Key]['enter']>>
:
never
type Machine_Concrete <Key, State> =
{
key: Key,
state: State,
}
export interface Machine <Sc extends Schema<any, any>>
{
key: States_Keys<Schema_States<Sc>>,
state: States_Values<Schema_States<Sc>>,
is <Key extends States_Keys<Schema_States<Sc>>> (key: Key): this is Machine_Narrow<this, Key>,
must <Key extends States_Keys<Schema_States<Sc>>> (key: Key): asserts this is Machine_Narrow<this, Key>,
can <Key extends States_Keys<Schema_States<Sc>>> (key: Key): boolean,
go <Key extends States_Keys<Schema_States<Sc>>> (key: Key, ...args: Parameters<Schema_Enter<Sc, Key>>): void,
when <Key extends States_Keys<Schema_States<Sc>>, P> (key: Key, fn: (state: ReturnType<Schema_Enter<Sc, Key>>) => P): P | undefined,
}
export function Machine
<
Sc extends Schema<States<any>, Paths<States<States_Map_Base>, Paths_Map_Base>>,
// Sc extends Schema<States<States_Map_Base>, Paths<States<States_Map_Base>, Paths_Map_Base>>,
Key extends States_Keys<Schema_States<Sc>>,
>
(schema: Sc, key: Key, ...args: Parameters<Schema_Enter<Sc, Key>>)
:
Machine<Sc>
{
if (! schema.states.has(key as any)) throw new TypeError('machine_wrong_init')
const $ =
{
key: void 0 as any,
state: void 0 as any,
is,
must,
can,
go,
when,
}
function is (key: Key)
{
return ($.key === key)
}
function must (key: Key)
{
if (! is(key)) throw new TypeError('machine_wrong_state')
}
function can (key: Key)
{
return schema.paths.has($.key, key as any)
}
function go (key: Key, ...args: Parameters<Schema_Enter<Sc, Key>>)
{
if (! schema.states.has(key as any)) throw new TypeError('machine_wrong_dst')
if (! can(key)) throw new TypeError('machine_impossible_path')
$leave($.key)
$enter(key, ...args)
}
function $leave (key: Key)
{
const { leave } = schema.states.get(key)
leave?.()
}
function $enter (key: Key, ...args: Parameters<Schema_Enter<Sc, Key>>)
{
const { enter } = schema.states.get(key)
const state = enter(...args)
$.key = key
$.state = state
}
function when <P> (key: Key, fn: (state: ReturnType<Schema_Enter<Sc, Key>>) => P): P | undefined
{
if (! is(key)) return
return fn($.state)
}
$enter(key, ...args)
return ($ as any)
}