ek-core 0.4.3

Business-agnostic plugin host and actor-over-bus runtime on top of ek-event: capability index with runtime service discovery, per-plugin actor workers, dynamic add/remove with lifecycle broadcasts, one-shot ask/ask_service with contract gates, panic quarantine, five-step close.
Documentation
//! 测试公共件:可编程 TestPlugin + 等待助手。仅测试使用。

#![allow(dead_code)]

use ek_core::{MAILBOX_CAP, Plugin, PluginContext, ServiceDesc};
use ek_event::Event;
use std::borrow::Cow;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};

pub type MailHook = Arc<dyn Fn(&Event) + Send + Sync>;
pub type VoidHook = Arc<dyn Fn() + Send + Sync>;
pub type InitHook = Arc<dyn Fn(&PluginContext) + Send + Sync>;

pub struct TestPlugin {
    name_: String,
    version_: String,
    deps_: Vec<&'static str>,
    routes_: Vec<Cow<'static, str>>,
    cap_: usize,
    services_: Vec<ServiceDesc>,
    on_mail_: Option<MailHook>,
    on_start_: Option<VoidHook>,
    on_stop_: Option<VoidHook>,
    on_init_: Option<InitHook>,
}

impl Plugin for TestPlugin {
    fn name(&self) -> &str {
        &self.name_
    }
    fn version(&self) -> Cow<'static, str> {
        Cow::Owned(self.version_.clone())
    }
    fn deps(&self) -> &[&str] {
        &self.deps_
    }
    fn routes(&self) -> Vec<Cow<'static, str>> {
        self.routes_.clone()
    }
    fn mailbox_cap(&self) -> usize {
        self.cap_
    }
    fn services(&self) -> Vec<ServiceDesc> {
        self.services_.clone()
    }
    fn on_init(&self, ctx: &PluginContext) {
        if let Some(f) = &self.on_init_ {
            f(ctx);
        }
    }
    fn on_start(&self) {
        if let Some(f) = &self.on_start_ {
            f();
        }
    }
    fn on_stop(&self) {
        if let Some(f) = &self.on_stop_ {
            f();
        }
    }
    fn on_mail(&self, event: &Event) {
        if let Some(f) = &self.on_mail_ {
            f(event);
        }
    }
}

pub struct TestPluginBuilder {
    inner: TestPlugin,
}

pub fn test_plugin(name: &str) -> TestPluginBuilder {
    TestPluginBuilder {
        inner: TestPlugin {
            name_: name.to_string(),
            version_: String::new(),
            deps_: Vec::new(),
            routes_: Vec::new(),
            cap_: MAILBOX_CAP,
            services_: Vec::new(),
            on_mail_: None,
            on_start_: None,
            on_stop_: None,
            on_init_: None,
        },
    }
}

impl TestPluginBuilder {
    pub fn version(mut self, v: &str) -> Self {
        self.inner.version_ = v.to_string();
        self
    }
    pub fn services(mut self, s: Vec<ServiceDesc>) -> Self {
        self.inner.services_ = s;
        self
    }
    pub fn deps(mut self, d: &[&'static str]) -> Self {
        self.inner.deps_ = d.to_vec();
        self
    }
    pub fn routes(mut self, r: Vec<Cow<'static, str>>) -> Self {
        self.inner.routes_ = r;
        self
    }
    pub fn routes_str(mut self, r: &[&'static str]) -> Self {
        self.inner.routes_ = r.iter().map(|s| Cow::Borrowed(*s)).collect();
        self
    }
    pub fn routes_owned(mut self, r: Vec<String>) -> Self {
        self.inner.routes_ = r.into_iter().map(Cow::Owned).collect();
        self
    }
    pub fn cap(mut self, c: usize) -> Self {
        self.inner.cap_ = c;
        self
    }
    pub fn mail<F: Fn(&Event) + Send + Sync + 'static>(mut self, f: F) -> Self {
        self.inner.on_mail_ = Some(Arc::new(f));
        self
    }
    pub fn start_hook<F: Fn() + Send + Sync + 'static>(mut self, f: F) -> Self {
        self.inner.on_start_ = Some(Arc::new(f));
        self
    }
    pub fn stop_hook<F: Fn() + Send + Sync + 'static>(mut self, f: F) -> Self {
        self.inner.on_stop_ = Some(Arc::new(f));
        self
    }
    pub fn init_hook<F: Fn(&PluginContext) + Send + Sync + 'static>(mut self, f: F) -> Self {
        self.inner.on_init_ = Some(Arc::new(f));
        self
    }
    pub fn build(self) -> Arc<TestPlugin> {
        Arc::new(self.inner)
    }
}

/// 轮询等待:deadline 内 fn() 返回 true 即成功;超时返回 false。
pub fn wait_until(timeout: Duration, mut f: impl FnMut() -> bool) -> bool {
    let deadline = Instant::now() + timeout;
    while Instant::now() < deadline {
        if f() {
            return true;
        }
        std::thread::sleep(Duration::from_millis(5));
    }
    f()
}

/// 新建共享计数器。
pub fn counter() -> Arc<AtomicU64> {
    Arc::new(AtomicU64::new(0))
}

pub fn bump(c: &AtomicU64) -> u64 {
    c.fetch_add(1, Ordering::AcqRel) + 1
}

pub fn load(c: &AtomicU64) -> u64 {
    c.load(Ordering::Acquire)
}

/// 新建共享记录向量。
pub fn records<T>() -> Arc<Mutex<Vec<T>>> {
    Arc::new(Mutex::new(Vec::new()))
}

pub fn push_record<T>(r: &Arc<Mutex<Vec<T>>>, v: T) {
    r.lock().unwrap().push(v);
}

pub fn snapshot<T: Clone>(r: &Arc<Mutex<Vec<T>>>) -> Vec<T> {
    r.lock().unwrap().clone()
}