///|
pub(all) enum RuntimeAction {
RuntimeEffect(String)
RuntimeEmit(EventTarget, Event)
RuntimeNavigate(String, ResolvedSource)
} derive(Debug, Eq)
///|
pub struct RuntimeLifecycleHook {
event : LifecycleEvent
actions : Array[RuntimeAction]
} derive(Debug, Eq)
///|
pub fn RuntimePlan::startup_actions(
self : RuntimePlan,
) -> Result[Array[RuntimeAction], Array[String]] {
self.lifecycle_actions(AppStarted)
}
///|
pub fn RuntimePlan::lifecycle_actions(
self : RuntimePlan,
event : LifecycleEvent,
) -> Result[Array[RuntimeAction], Array[String]] {
let actions : Array[RuntimeAction] = []
let problems : Array[String] = []
if event is AppStarted {
self.collect_actions(self.launch.startup(), actions, problems)
}
for hook in self.launch.lifecycle_hooks() {
if hook.event() == event {
self.collect_actions(hook.command(), actions, problems)
}
}
if problems.is_empty() {
Ok(actions)
} else {
Err(problems)
}
}
///|
pub fn RuntimePlan::lifecycle_hooks(
self : RuntimePlan,
) -> Result[Array[RuntimeLifecycleHook], Array[String]] {
let events : Array[LifecycleEvent] = []
if !self.launch.startup().is_empty() {
events.push(AppStarted)
}
for hook in self.launch.lifecycle_hooks() {
let event = hook.event()
if !events.contains(event) {
events.push(event)
}
}
let hooks : Array[RuntimeLifecycleHook] = []
let problems : Array[String] = []
for event in events {
match self.lifecycle_actions(event) {
Ok(actions) =>
if !actions.is_empty() {
hooks.push(RuntimeLifecycleHook::new(event, actions))
}
Err(action_problems) =>
for problem in action_problems {
problems.push(problem)
}
}
}
if problems.is_empty() {
Ok(hooks)
} else {
Err(problems)
}
}
///|
pub fn RuntimePlan::actions(
self : RuntimePlan,
command : Cmd,
) -> Result[Array[RuntimeAction], Array[String]] {
let actions : Array[RuntimeAction] = []
let problems : Array[String] = []
self.collect_actions(command, actions, problems)
if problems.is_empty() {
Ok(actions)
} else {
Err(problems)
}
}
///|
fn RuntimePlan::collect_actions(
self : RuntimePlan,
command : Cmd,
actions : Array[RuntimeAction],
problems : Array[String],
) -> Unit {
match command {
None => ()
Batch(commands) =>
for command in commands {
self.collect_actions(command, actions, problems)
}
Effect(name) =>
if name == "" {
problems.push("effect name is required")
} else {
actions.push(RuntimeEffect(name))
}
Emit(target, event) => {
let event_problems = event.validate()
for problem in event_problems {
problems.push(problem)
}
if event_problems.is_empty() {
actions.push(RuntimeEmit(target, event))
}
}
Navigate(window_label, source) =>
if window_label == "" {
problems.push("navigate window label is required")
} else if !self.has_window(window_label) {
problems.push("navigate window not found: \{window_label}")
} else {
match
source.resolve(window_label~, asset_protocol=self.asset_protocol) {
Ok(resolved) => actions.push(RuntimeNavigate(window_label, resolved))
Err(problem) => problems.push(problem)
}
}
}
}
///|
fn RuntimePlan::has_window(self : RuntimePlan, label : String) -> Bool {
for window in self.windows {
if window.label() == label {
return true
}
}
false
}
///|
pub fn RuntimeLifecycleHook::new(
event : LifecycleEvent,
actions : Array[RuntimeAction],
) -> RuntimeLifecycleHook {
{ event, actions }
}
///|
pub fn RuntimeLifecycleHook::event(
self : RuntimeLifecycleHook,
) -> LifecycleEvent {
self.event
}
///|
pub fn RuntimeLifecycleHook::actions(
self : RuntimeLifecycleHook,
) -> Array[RuntimeAction] {
self.actions.copy()
}
///|
pub fn RuntimeLifecycleHook::to_json(self : RuntimeLifecycleHook) -> String {
[
"{",
"\"event\":\{self.event.to_json()},",
"\"actions\":[\{self.actions.map(fn(action) { action.to_json() }).join(",")}]",
"}",
].join("")
}
///|
pub fn RuntimeAction::to_json(self : RuntimeAction) -> String {
match self {
RuntimeEffect(name) =>
["{", "\"kind\":\"effect\",", "\"name\":\{name.json_string()}", "}"].join(
"",
)
RuntimeEmit(target, event) =>
[
"{",
"\"kind\":\"emit\",",
"\"target\":\{target.to_json()},",
"\"event\":\{event.to_json()}",
"}",
].join("")
RuntimeNavigate(window_label, source) =>
[
"{",
"\"kind\":\"navigate\",",
"\"windowLabel\":\{window_label.json_string()},",
"\"url\":\{source.url().json_string()},",
"\"protocolMappings\":[\{source.protocol_mappings().map(protocol_mapping_json).join(",")}],",
"\"virtualFiles\":[\{source.virtual_files().map(virtual_file_json).join(",")}]",
"}",
].join("")
}
}
///|
pub fn LifecycleEvent::to_json(self : LifecycleEvent) -> String {
match self {
AppStarted => "{\"kind\":\"app-started\"}"
AppWillExit => "{\"kind\":\"app-will-exit\"}"
PluginSetup(name) => plugin_lifecycle_event_json("plugin-setup", name)
PluginReady(name) => plugin_lifecycle_event_json("plugin-ready", name)
PluginWillExit(name) =>
plugin_lifecycle_event_json("plugin-will-exit", name)
WindowCloseRequested(label) =>
[
"{",
"\"kind\":\"window-close-requested\",",
"\"label\":\{label.json_string()}",
"}",
].join("")
WindowClosed(label) =>
[
"{",
"\"kind\":\"window-closed\",",
"\"label\":\{label.json_string()}",
"}",
].join("")
}
}
///|
fn plugin_lifecycle_event_json(kind : String, name : String) -> String {
[
"{",
"\"kind\":\{kind.json_string()},",
"\"plugin\":\{name.json_string()}",
"}",
].join("")
}
///|
pub fn EventTarget::to_json(self : EventTarget) -> String {
match self {
AppTarget => "{\"kind\":\"app\"}"
WindowTarget(label) =>
["{", "\"kind\":\"window\",", "\"label\":\{label.json_string()}", "}"].join(
"",
)
WebviewTarget(label) =>
["{", "\"kind\":\"webview\",", "\"label\":\{label.json_string()}", "}"].join(
"",
)
}
}
///|
fn protocol_mapping_json(mapping : ProtocolMapping) -> String {
[
"{",
"\"scheme\":\{mapping.scheme().json_string()},",
"\"root\":\{mapping.root().json_string()}",
"}",
].join("")
}
///|
fn virtual_file_json(file : VirtualFile) -> String {
[
"{",
"\"path\":\{file.path().json_string()},",
"\"mimeType\":\{mime_type_for_path(file.path()).json_string()},",
"\"content\":\{file.content().json_string()}",
"}",
].join("")
}