use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::cell::RefCell;
use super::executor::{self, Task};
use super::{Caller, Context, DaemonPlugin};
use crate::api::Json;
use crate::error::Result;
use crate::ui_runtime::Event;
crate::local::instance_local! {
fn caller_slot() -> Option<Caller> = None;
}
crate::local::instance_local! {
fn interest_slot() -> super::Interest = super::Interest::default();
}
pub(crate) fn current_interest() -> super::Interest {
interest_slot(|slot| slot.clone())
}
pub(crate) fn current_caller() -> Option<Caller> {
caller_slot(|slot| slot.clone())
}
struct State<P: 'static> {
plugin: &'static P,
tasks: Vec<Task>,
cx: Context,
}
#[doc(hidden)]
pub struct DaemonRuntime<P: 'static> {
state: RefCell<Option<State<P>>>,
}
unsafe impl<P> Sync for DaemonRuntime<P> {}
impl<P> Default for DaemonRuntime<P> {
fn default() -> Self {
Self::new()
}
}
impl<P> DaemonRuntime<P> {
pub const fn new() -> Self {
Self {
state: RefCell::new(None),
}
}
}
impl<P: DaemonPlugin> DaemonRuntime<P> {
pub fn activate(&self, config: String) {
self.deactivate();
interest_slot(|slot| *slot = super::Interest::default());
let cx = Context::new(config);
let plugin: &'static P = Box::leak(Box::new(P::activate(&cx)));
let run = plugin.run(cx.clone());
*self.state.borrow_mut() = Some(State {
plugin,
tasks: alloc::vec![Task::new(Box::pin(run))],
cx,
});
}
pub fn event(&self, event: Event) {
if let Event::Plugin { name, payload } = &event
&& name == super::INTEREST_EVENT
{
let Some(interest) = super::Interest::from_payload(&payload.0) else {
return;
};
let changed = interest_slot(|slot| {
let changed = *slot != interest;
*slot = interest.clone();
changed
});
if changed {
executor::push_event(Event::Interest(interest));
}
return;
}
executor::push_event(event);
}
pub fn drive(&self, now_ms: u64) -> Option<u64> {
let mut tasks = core::mem::take(&mut self.state.borrow_mut().as_mut()?.tasks);
let next = executor::run(&mut tasks, now_ms);
if let Some(state) = self.state.borrow_mut().as_mut() {
state.tasks.append(&mut tasks);
}
next
}
pub fn handle_call(&self, method: &str, payload: String, caller: Caller) -> Result<String> {
let (plugin, cx) = match self.state.borrow().as_ref() {
Some(state) => (state.plugin, state.cx.clone()),
None => {
return Err(crate::Error::Unavailable("not active".into()));
}
};
caller_slot(|slot| *slot = Some(caller));
let answer = plugin
.call(method, Json(payload), &cx)
.map(Json::into_string);
caller_slot(|slot| *slot = None);
answer
}
pub fn with_plugin<R>(&self, f: impl FnOnce(&P) -> R) -> Option<R> {
let plugin = self.state.borrow().as_ref().map(|state| state.plugin)?;
Some(f(plugin))
}
pub fn deactivate(&self) {
let Some(state) = self.state.borrow_mut().take() else {
return;
};
let State { plugin, tasks, .. } = state;
drop(tasks);
executor::reset();
plugin.deactivate();
drop(unsafe { Box::from_raw(core::ptr::from_ref(plugin).cast_mut()) });
}
}
impl From<crate::bindings::daemon::exports::standard::plugin::daemon_events::DaemonEvent>
for Event
{
fn from(
event: crate::bindings::daemon::exports::standard::plugin::daemon_events::DaemonEvent,
) -> Self {
use super::PaneChangeKind;
use crate::bindings::daemon::exports::standard::plugin::daemon_events::{
DaemonEvent as Wit, PaneChangeKind as WitKind,
};
match event {
Wit::FileChanged(change) => Self::FileChanged {
watch: change.watch,
paths: change.paths,
},
Wit::WatchFailed((watch, error)) => Self::WatchFailed { watch, error },
Wit::ProcessOutput(output) => Self::ProcessOutput {
pid: output.pid,
stderr: output.stderr,
bytes: output.bytes,
},
Wit::ProcessExited((pid, status)) => Self::ProcessExited { pid, status },
Wit::PaneChanged(change) => Self::PaneChanged {
kind: match change.kind {
WitKind::Created => PaneChangeKind::Created,
WitKind::Changed => PaneChangeKind::Changed,
WitKind::Exited => PaneChangeKind::Exited,
WitKind::Closed => PaneChangeKind::Closed,
},
pane: change.pane.into(),
},
}
}
}
impl From<crate::bindings::daemon::exports::standard::plugin::companion::Caller> for Caller {
fn from(caller: crate::bindings::daemon::exports::standard::plugin::companion::Caller) -> Self {
Self {
machine_id: caller.machine_id,
plugin_id: caller.plugin_id,
kind: caller.kind,
epoch: caller.epoch,
controller_machine_id: caller.controller_machine_id,
controller_fencing_token: caller.controller_fencing_token,
}
}
}