use std::borrow::Cow;
use std::collections::VecDeque;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, OnceLock};
use std::time::{Duration, Instant};
use ek_core::context::PluginContext;
use ek_core::plugin::Plugin;
use ek_event::Event;
pub struct Probe {
name: &'static str,
types: Vec<String>,
events: Arc<Mutex<Vec<String>>>,
}
impl Probe {
pub fn with_types(name: &'static str, types: &[&str], events: Arc<Mutex<Vec<String>>>) -> Self {
Self {
name,
types: types.iter().map(|t| (*t).to_string()).collect(),
events,
}
}
}
impl Plugin for Probe {
fn name(&self) -> &str {
self.name
}
fn routes(&self) -> Vec<Cow<'static, str>> {
self.types.iter().map(|t| Cow::Owned(t.clone())).collect()
}
fn on_mail(&self, event: &Event) {
self.events.lock().unwrap().push(event.event_type.clone());
}
}
pub struct AskStep {
pub exec: Box<dyn Fn(&PluginContext) -> String + Send + Sync>,
}
pub struct Driver {
cursor: AtomicUsize,
steps: Vec<AskStep>,
outputs: Mutex<VecDeque<String>>,
}
impl Driver {
pub fn with_steps(initial: usize, steps: Vec<AskStep>) -> Self {
Self {
cursor: AtomicUsize::new(initial),
steps,
outputs: Mutex::new(VecDeque::new()),
}
}
pub fn pop_output(&self) -> Option<String> {
self.outputs.lock().unwrap().pop_front()
}
pub fn outputs_left(&self) -> Vec<String> {
self.outputs.lock().unwrap().iter().cloned().collect()
}
fn on_go(&self, ctx: &PluginContext) {
let idx = self.cursor.fetch_add(1, Ordering::AcqRel);
if let Some(step) = self.steps.get(idx) {
let out = (step.exec)(ctx);
self.outputs.lock().unwrap().push_back(out);
}
}
}
pub struct Asker {
name: &'static str,
driver: Arc<Driver>,
ctx: OnceLock<PluginContext>,
}
impl Asker {
pub fn driven(name: &'static str, driver: Arc<Driver>) -> Self {
Self {
name,
driver,
ctx: OnceLock::new(),
}
}
}
impl Plugin for Asker {
fn name(&self) -> &str {
self.name
}
fn routes(&self) -> Vec<Cow<'static, str>> {
vec![Cow::Borrowed("eGo.")]
}
fn on_init(&self, ctx: &PluginContext) {
let _ = self.ctx.set(ctx.clone());
}
fn on_mail(&self, event: &Event) {
if event.event_type != "eGo." {
return;
}
if let Some(ctx) = self.ctx.get() {
self.driver.on_go(ctx);
}
}
}
pub struct Stopper {
name: &'static str,
deps: Vec<&'static str>,
stops: Arc<Mutex<Vec<String>>>,
}
impl Stopper {
pub fn with_deps(
name: &'static str,
deps: &[&'static str],
stops: Arc<Mutex<Vec<String>>>,
) -> Self {
Self {
name,
deps: deps.to_vec(),
stops,
}
}
}
impl Plugin for Stopper {
fn name(&self) -> &str {
self.name
}
fn deps(&self) -> &[&str] {
&self.deps
}
fn on_stop(&self) {
self.stops.lock().unwrap().push(self.name.to_string());
}
fn on_mail(&self, _event: &Event) {} }
pub fn wait_until(tag: &str, secs: u64, mut f: impl FnMut() -> bool) {
let deadline = Instant::now() + Duration::from_secs(secs);
while Instant::now() < deadline {
if f() {
return;
}
std::thread::sleep(Duration::from_millis(10));
}
panic!("wait_until timeout: {tag}");
}