///|
let runtime_control_plugin = "__lepusa"
///|
fn InvokeRequest::is_runtime_control(self : InvokeRequest) -> Bool {
self.plugin() == runtime_control_plugin
}
///|
fn AppManager::dispatch_runtime_control(
self : AppManager,
request : InvokeRequest,
) -> InvokeResponse? {
if !request.is_runtime_control() {
return None
}
match request.command() {
"event.emit" => Some(self.dispatch_runtime_event_emit(request))
"stream.drain" => Some(self.dispatch_runtime_stream_drain(request))
"stream.cancel" => Some(self.dispatch_runtime_stream_cancel(request))
"resource.close" => Some(self.dispatch_runtime_resource_close(request))
command =>
Some(
InvokeResponse::unknown_command(
request.id(),
"\{runtime_control_plugin}.\{command}",
),
)
}
}
///|
fn AppManager::dispatch_runtime_event_emit(
self : AppManager,
request : InvokeRequest,
) -> InvokeResponse {
match runtime_control_object(request) {
Err(response) => response
Ok(object) => {
let name = runtime_control_string_or(object, "name", "event")
if name == "" {
return InvokeResponse::invalid_request(
request.id(),
"runtime event name is required",
)
}
match runtime_control_event_target(object.get("target")) {
Err(problem) => InvokeResponse::invalid_request(request.id(), problem)
Ok(target) =>
match runtime_control_authorize_event_target(request, target) {
Some(response) => response
None =>
match
self.emit(
Event::new(name, payload=runtime_control_payload(object)),
target~,
) {
Ok(deliveries) =>
InvokeResponse::ok(
request.id(),
"{\"delivered\":\{deliveries.length()}}",
)
Err(problems) =>
InvokeResponse::handler_error(
request.id(),
problems.join("; "),
route=request.route(),
)
}
}
}
}
}
}
///|
fn AppManager::dispatch_runtime_stream_drain(
self : AppManager,
request : InvokeRequest,
) -> InvokeResponse {
match runtime_control_channel_id(request) {
Err(response) => response
Ok(channel_id) =>
match self.runtime_control_authorize_resource(request, channel_id) {
Some(response) => response
None =>
match self.drain_channel(channel_id) {
Ok(messages) =>
InvokeResponse::ok(
request.id(),
[
"{",
"\"channelId\":\{channel_id.json_string()},",
"\"messages\":[\{messages.map(fn(message) { message.to_json() }).join(",")}]",
"}",
].join(""),
)
Err(problem) =>
InvokeResponse::handler_error(
request.id(),
problem,
route=request.route(),
)
}
}
}
}
///|
fn AppManager::dispatch_runtime_stream_cancel(
self : AppManager,
request : InvokeRequest,
) -> InvokeResponse {
match runtime_control_channel_id(request) {
Err(response) => response
Ok(channel_id) =>
match self.runtime_control_authorize_resource(request, channel_id) {
Some(response) => response
None =>
match self.cancel_channel(channel_id) {
Ok(message) =>
InvokeResponse::ok(
request.id(),
[
"{",
"\"channelId\":\{channel_id.json_string()},",
"\"message\":\{message.to_json()}",
"}",
].join(""),
)
Err(problem) =>
InvokeResponse::handler_error(
request.id(),
problem,
route=request.route(),
)
}
}
}
}
///|
fn AppManager::dispatch_runtime_resource_close(
self : AppManager,
request : InvokeRequest,
) -> InvokeResponse {
match runtime_control_object(request) {
Err(response) => response
Ok(object) => {
let id = runtime_control_string_or(object, "id", "channelId")
if id == "" {
return InvokeResponse::invalid_request(
request.id(),
"runtime resource id is required",
)
}
match self.runtime_control_authorize_resource(request, id) {
Some(response) => response
None => {
let result = match runtime_control_string(object, "kind") {
Some(kind) =>
self.close_resource_handle(ResourceHandle::new(id~, kind~))
None => self.close_resource(id)
}
match result {
Ok(resource) =>
InvokeResponse::ok(
request.id(),
["{", "\"closed\":\{resource.to_json()}", "}"].join(""),
)
Err(problem) =>
InvokeResponse::handler_error(
request.id(),
problem,
route=request.route(),
)
}
}
}
}
}
}
///|
fn AppManager::runtime_control_authorize_resource(
self : AppManager,
request : InvokeRequest,
resource_id : String,
) -> InvokeResponse? {
match self.resource(resource_id) {
None => None
Some(resource) =>
match runtime_control_resource_window(resource) {
Some(window_label) =>
if window_label == request.window_label() {
None
} else {
Some(
InvokeResponse::permission_denied(request.id(), request.route()),
)
}
None =>
Some(InvokeResponse::permission_denied(request.id(), request.route()))
}
}
}
///|
fn runtime_control_authorize_event_target(
request : InvokeRequest,
target : EventTarget,
) -> InvokeResponse? {
match target {
AppTarget => None
WindowTarget(label) | WebviewTarget(label) =>
if label == request.window_label() {
None
} else {
Some(InvokeResponse::permission_denied(request.id(), request.route()))
}
}
}
///|
fn runtime_control_resource_window(resource : ResourceEntry) -> String? {
let parsed = @json.parse(resource.metadata()) catch { _ => return None }
match parsed {
Object(object) => runtime_control_string(object, "window")
_ => None
}
}
///|
fn runtime_control_channel_id(
request : InvokeRequest,
) -> Result[String, InvokeResponse] {
match runtime_control_object(request) {
Err(response) => Err(response)
Ok(object) => {
let channel_id = runtime_control_string_or(object, "channelId", "id")
if channel_id == "" {
Err(
InvokeResponse::invalid_request(
request.id(),
"runtime stream channelId is required",
),
)
} else {
Ok(channel_id)
}
}
}
}
///|
fn runtime_control_object(
request : InvokeRequest,
) -> Result[Map[String, Json], InvokeResponse] {
let parsed = @json.parse(request.payload()) catch {
error =>
return Err(
InvokeResponse::invalid_request(
request.id(),
"invalid runtime control json: \{error}",
),
)
}
match parsed {
Object(object) => Ok(object)
_ =>
Err(
InvokeResponse::invalid_request(
request.id(),
"runtime control payload must be an object",
),
)
}
}
///|
fn runtime_control_string(object : Map[String, Json], key : String) -> String? {
match object.get(key) {
Some(String(value)) => Some(value)
_ => None
}
}
///|
fn runtime_control_string_or(
object : Map[String, Json],
primary : String,
fallback : String,
) -> String {
match runtime_control_string(object, primary) {
Some(value) => value
None =>
match runtime_control_string(object, fallback) {
Some(value) => value
None => ""
}
}
}
///|
fn runtime_control_payload(object : Map[String, Json]) -> String {
match object.get("payload") {
None | Some(Null) => ""
Some(String(value)) => value
Some(value) => value.stringify()
}
}
///|
fn runtime_control_event_target(value : Json?) -> Result[EventTarget, String] {
match value {
None | Some(Null) => Ok(AppTarget)
Some(String(label)) =>
if label == "" {
Err("runtime event target label is required")
} else {
Ok(WindowTarget(label))
}
Some(Object(object)) => {
let kind = match object.get("kind") {
Some(String(value)) => value.to_lower()
_ => "app"
}
let label = match object.get("label") {
Some(String(value)) => value
_ => ""
}
match kind {
"app" => Ok(AppTarget)
"window" =>
if label == "" {
Err("runtime event target window label is required")
} else {
Ok(WindowTarget(label))
}
"webview" =>
if label == "" {
Err("runtime event target webview label is required")
} else {
Ok(WebviewTarget(label))
}
_ => Err("unknown runtime event target kind: \{kind}")
}
}
_ => Err("runtime event target must be an object, string, or null")
}
}