///|
pub(all) enum CommandMode {
Sync
Async
Stream
Event
} derive(Debug, Eq)
///|
pub fn CommandMode::name(self : CommandMode) -> String {
match self {
Sync => "sync"
Async => "async"
Stream => "stream"
Event => "event"
}
}
///|
pub(all) enum CommandExecutionAffinity {
MainThread
WorkerThread
AnyThread
} derive(Debug, Eq)
///|
pub fn CommandExecutionAffinity::name(
self : CommandExecutionAffinity,
) -> String {
match self {
MainThread => "main-thread"
WorkerThread => "worker-thread"
AnyThread => "any-thread"
}
}
///|
pub struct CommandSpec {
name : String
mode : CommandMode
affinity : CommandExecutionAffinity
permission : Permission
request_schema : IpcSchema
response_schema : IpcSchema
} derive(Debug, Eq)
///|
pub fn CommandSpec::new(
name : String,
mode? : CommandMode = Sync,
affinity? : CommandExecutionAffinity = AnyThread,
permission? : Permission = Permission::command(name),
request? : IpcSchema = IpcSchema::any(),
response? : IpcSchema = IpcSchema::any(),
) -> CommandSpec {
{
name,
mode,
affinity,
permission,
request_schema: request,
response_schema: response,
}
}
///|
pub fn CommandSpec::name(self : CommandSpec) -> String {
self.name
}
///|
pub fn CommandSpec::mode(self : CommandSpec) -> CommandMode {
self.mode
}
///|
pub fn CommandSpec::affinity(self : CommandSpec) -> CommandExecutionAffinity {
self.affinity
}
///|
pub fn CommandSpec::permission(self : CommandSpec) -> Permission {
self.permission
}
///|
pub fn CommandSpec::request_schema(self : CommandSpec) -> IpcSchema {
self.request_schema
}
///|
pub fn CommandSpec::response_schema(self : CommandSpec) -> IpcSchema {
self.response_schema
}
///|
pub fn CommandSpec::with_schema(
self : CommandSpec,
request? : IpcSchema = self.request_schema,
response? : IpcSchema = self.response_schema,
) -> CommandSpec {
{ ..self, request_schema: request, response_schema: response }
}
///|
pub fn CommandSpec::with_affinity(
self : CommandSpec,
affinity : CommandExecutionAffinity,
) -> CommandSpec {
{ ..self, affinity, }
}
///|
pub struct Plugin {
name : String
commands : Array[CommandSpec]
lifecycle_hooks : Array[LifecycleHook]
} derive(Debug, Eq)
///|
pub fn Plugin::new(name : String) -> Plugin {
{ name, commands: [], lifecycle_hooks: [] }
}
///|
pub fn Plugin::command(
self : Plugin,
name : String,
mode? : CommandMode = Sync,
affinity? : CommandExecutionAffinity = AnyThread,
permission? : Permission,
request? : IpcSchema,
response? : IpcSchema,
) -> Plugin {
let commands = self.commands.copy()
let route = "\{self.name}.\{name}"
let permission = permission.unwrap_or(Permission::command(route))
let request = request.unwrap_or(IpcSchema::any())
let response = response.unwrap_or(IpcSchema::any())
commands.push(
CommandSpec::new(name, mode~, affinity~, permission~, request~, response~),
)
{ ..self, commands, }
}
///|
pub fn Plugin::command_sync(
self : Plugin,
name : String,
permission? : Permission,
request? : IpcSchema,
response? : IpcSchema,
affinity? : CommandExecutionAffinity = AnyThread,
) -> Plugin {
self.command(name, mode=Sync, affinity~, permission?, request?, response?)
}
///|
pub fn Plugin::command_async(
self : Plugin,
name : String,
permission? : Permission,
request? : IpcSchema,
response? : IpcSchema,
affinity? : CommandExecutionAffinity = AnyThread,
) -> Plugin {
self.command(name, mode=Async, affinity~, permission?, request?, response?)
}
///|
pub fn Plugin::command_stream(
self : Plugin,
name : String,
permission? : Permission,
request? : IpcSchema,
response? : IpcSchema,
affinity? : CommandExecutionAffinity = AnyThread,
) -> Plugin {
self.command(name, mode=Stream, affinity~, permission?, request?, response?)
}
///|
pub fn Plugin::command_event(
self : Plugin,
name : String,
permission? : Permission,
request? : IpcSchema,
response? : IpcSchema,
affinity? : CommandExecutionAffinity = AnyThread,
) -> Plugin {
self.command(name, mode=Event, affinity~, permission?, request?, response?)
}
///|
pub fn Plugin::name(self : Plugin) -> String {
self.name
}
///|
pub fn Plugin::commands(self : Plugin) -> Array[CommandSpec] {
self.commands.copy()
}
///|
pub fn Plugin::with_lifecycle(
self : Plugin,
event : LifecycleEvent,
command : Cmd,
) -> Plugin {
let lifecycle_hooks = self.lifecycle_hooks.copy()
lifecycle_hooks.push(LifecycleHook::new(event, command))
{ ..self, lifecycle_hooks, }
}
///|
pub fn Plugin::on_setup(self : Plugin, command : Cmd) -> Plugin {
self.with_lifecycle(PluginSetup(self.name), command)
}
///|
pub fn Plugin::on_ready(self : Plugin, command : Cmd) -> Plugin {
self.with_lifecycle(PluginReady(self.name), command)
}
///|
pub fn Plugin::on_will_exit(self : Plugin, command : Cmd) -> Plugin {
self.with_lifecycle(PluginWillExit(self.name), command)
}
///|
pub fn Plugin::lifecycle_hooks(self : Plugin) -> Array[LifecycleHook] {
self.lifecycle_hooks.copy()
}
///|
pub fn Plugin::command_names(self : Plugin) -> Array[String] {
self.commands.map(fn(command) { command.name() })
}
///|
pub fn Plugin::command_routes(self : Plugin) -> Array[String] {
self.commands.map(fn(command) { "\{self.name}.\{command.name()}" })
}
///|
pub fn Plugin::validate(self : Plugin) -> Array[String] {
let problems : Array[String] = []
if self.name == "" {
problems.push("plugin name is required")
}
let seen : Array[String] = []
for command in self.commands {
let name = command.name()
if name == "" {
problems.push("plugin command name is required")
} else if seen.contains(name) {
problems.push("plugin command must be unique: \{name}")
} else {
seen.push(name)
}
for problem in command.request_schema().validate() {
problems.push(problem)
}
for problem in command.response_schema().validate() {
problems.push(problem)
}
}
for hook in self.lifecycle_hooks {
match hook.event().plugin_name() {
Some(name) =>
if name != self.name {
problems.push(
"plugin lifecycle hook belongs to another plugin: \{name}",
)
}
None => problems.push("plugin lifecycle hook must use a plugin event")
}
}
problems
}