-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathengine.go
More file actions
225 lines (189 loc) · 8.58 KB
/
Copy pathengine.go
File metadata and controls
225 lines (189 loc) · 8.58 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
package script_engine
import (
"context"
"errors"
"github.com/tx7do/go-scripts/source"
)
// ErrCapabilityNotSupported is returned when an engine does not implement an
// optional capability interface (e.g. ModuleRegistrar, ScriptWatcher).
//
// Callers that receive this error should gracefully degrade instead of
// treating it as a fatal failure.
var ErrCapabilityNotSupported = errors.New("script engine: capability not supported by this engine type")
////////////////////////////////////////////////////////////////////////////////////////////
// Core Lifecycle — ALL engines must implement this interface.
////////////////////////////////////////////////////////////////////////////////////////////
// ScriptEngine is the minimal lifecycle interface that every engine
// implementation — regardless of its feature set — must satisfy.
//
// Full-featured engines (Lua, JavaScript) additionally implement the optional
// capability interfaces below and are aggregated by [Engine].
// Lightweight engines (CEL, Expr) may implement only ScriptEngine +
// [ScriptExecutor] and skip the rest.
type ScriptEngine interface {
// GetType returns the Type identifier of this script engine.
GetType() Type
// Init initializes the script engine. Must be called before any Load*/Execute*.
// Returns an error if initialization fails or if the engine is already initialized.
Init(ctx context.Context) error
// Close releases all resources held by the engine (runtime, VM, handles).
// Returns an error if teardown fails. After Close, the engine must be re-Init'd
// before reuse.
Close() error
// IsInitialized reports whether the engine has been initialized and not yet closed.
IsInitialized() bool
// GetLastError returns the last error recorded by the engine, or nil if none.
GetLastError() error
// ClearError clears the engine's last-error state.
ClearError()
}
////////////////////////////////////////////////////////////////////////////////////////////
// Capability Interfaces — each represents an orthogonal feature set.
// Engines implement only the ones they support.
////////////////////////////////////////////////////////////////////////////////////////////
// ScriptLoader provides source-driven script loading.
//
// Loading is uniformly driven by the bound ScriptSource so the engine itself
// stays decoupled from concrete IO mechanisms (filesystem, S3, memory, ...).
type ScriptLoader interface {
// SetSource binds a ScriptSource (FileSource / S3 / Mem / Multi / ...) to the
// engine. Subsequent Load / ExecuteFromKey / ExecuteFromKeys calls read through it.
// Passing nil clears any previously bound source.
SetSource(source source.Reader)
// GetSource returns the currently bound ScriptSource, or nil if none has been set.
GetSource() source.Reader
// Load loads a single script from the bound Source using the given key
// (path / object key / script id, ...). Loaded scripts are kept by the engine
// for later Execute runs.
Load(ctx context.Context, key string) error
// LoadMulti loads multiple scripts from the bound Source in order.
// It aborts on the first error.
LoadMulti(ctx context.Context, keys []string) error
// LoadString compiles an inline script given directly as a string. It does NOT
// go through the bound Source; use Load/LoadMulti for source-driven loading.
// `name` is used for diagnostics (stack traces, error messages).
LoadString(ctx context.Context, name string, code string) error
}
// ScriptExecutor provides script execution capabilities.
type ScriptExecutor interface {
// Execute runs every script previously loaded via Load/LoadMulti/LoadString and
// returns the combined result.
Execute(ctx context.Context) (any, error)
// ExecuteFromKey loads the script identified by `key` from the bound Source and
// immediately runs it, all in one step.
ExecuteFromKey(ctx context.Context, key string) (any, error)
// ExecuteFromKeys is the multi-key variant of ExecuteFromKey; results are
// returned in the same order as `keys`.
ExecuteFromKeys(ctx context.Context, keys []string) ([]any, error)
// ExecuteString compiles and immediately runs an inline string script, bypassing
// the bound Source. `name` is used for diagnostics.
ExecuteString(ctx context.Context, name string, code string) (any, error)
}
// GlobalAccessor provides read/write access to global variables visible to scripts.
type GlobalAccessor interface {
// RegisterGlobal registers or overwrites a global variable visible to scripts.
RegisterGlobal(name string, value any) error
// GetGlobal reads the value of a global variable. Returns an error if the name
// is undefined.
GetGlobal(name string) (any, error)
}
// FunctionRegistrar provides host-function registration and script-function invocation.
type FunctionRegistrar interface {
// RegisterFunction registers a host function that scripts can call by `name`.
// The concrete type accepted for `fn` depends on the engine implementation.
RegisterFunction(name string, fn any) error
// CallFunction invokes the script-side function registered as `name` with the
// given arguments and returns its result. ctx can be used to cancel/timeout
// the call.
CallFunction(ctx context.Context, name string, args ...any) (any, error)
}
// ModuleRegistrar provides module registration for engines that support a
// module system (e.g. Lua's require, JavaScript's import).
//
// Lightweight expression engines (CEL, Expr) typically do NOT implement this.
type ModuleRegistrar interface {
// RegisterModule registers a module (e.g. map[string]any, native loader, ...)
// under `name` so scripts can require/use it. The accepted shape of `module`
// depends on the engine implementation.
RegisterModule(name string, module any) error
}
// ScriptWatcher provides hot-reload (Watch) capabilities for scripts bound via
// a Source that implements [source.Watcher].
//
// Engines that do not support hot-reload (CEL, Expr, ...) do NOT implement this.
type ScriptWatcher interface {
// StartWatch starts watching the script identified by `key` for changes via the
// bound Source's Watch capability. When a change is detected, the script is
// automatically reloaded. Returns an error if the source doesn't implement
// Watcher or is not bound.
StartWatch(ctx context.Context, key string) error
// StopWatch stops watching the script identified by `key` and cleans up
// the associated goroutine.
StopWatch(key string) error
}
////////////////////////////////////////////////////////////////////////////////////////////
// Aggregate Interface — full-featured engines implement this.
////////////////////////////////////////////////////////////////////////////////////////////
// Engine is the aggregate interface combining all capability interfaces.
// Full-featured engines (Lua, JavaScript, ...) implement this.
//
// Lightweight engines (CEL, Expr) may implement only a subset; callers should
// use the capability helper functions (AsLoader, AsExecutor, ...) or direct
// type assertions to check for support.
//
// Implementations are expected to be safe for concurrent use of the methods
// documented as such; see each method's comment for details.
type Engine interface {
ScriptEngine
ScriptLoader
ScriptExecutor
GlobalAccessor
FunctionRegistrar
ModuleRegistrar
ScriptWatcher
}
////////////////////////////////////////////////////////////////////////////////////////////
// Capability Helpers — convenience functions for safe type assertions.
////////////////////////////////////////////////////////////////////////////////////////////
// AsLoader returns the ScriptLoader capability of e, or nil if unsupported.
func AsLoader(e any) ScriptLoader {
if l, ok := e.(ScriptLoader); ok {
return l
}
return nil
}
// AsExecutor returns the ScriptExecutor capability of e, or nil if unsupported.
func AsExecutor(e any) ScriptExecutor {
if ex, ok := e.(ScriptExecutor); ok {
return ex
}
return nil
}
// AsGlobalAccessor returns the GlobalAccessor capability of e, or nil if unsupported.
func AsGlobalAccessor(e any) GlobalAccessor {
if g, ok := e.(GlobalAccessor); ok {
return g
}
return nil
}
// AsFunctionRegistrar returns the FunctionRegistrar capability of e, or nil if unsupported.
func AsFunctionRegistrar(e any) FunctionRegistrar {
if f, ok := e.(FunctionRegistrar); ok {
return f
}
return nil
}
// AsModuleRegistrar returns the ModuleRegistrar capability of e, or nil if unsupported.
func AsModuleRegistrar(e any) ModuleRegistrar {
if m, ok := e.(ModuleRegistrar); ok {
return m
}
return nil
}
// AsWatcher returns the ScriptWatcher capability of e, or nil if unsupported.
func AsWatcher(e any) ScriptWatcher {
if w, ok := e.(ScriptWatcher); ok {
return w
}
return nil
}