ek-core 0.1.0

Business-agnostic plugin host and actor-over-bus runtime on top of ek-event: single-registry plugins, explicit deps with topological start, per-plugin actor workers, one-shot request/reply, panic quarantine, five-step close.
Documentation
//! 内置通用日志插件(公用层,宿主级,零业务)。
//!
//! 事件:`EVENT_LOG`(载荷 `LogData`);任何 ctx.write_log → 总线 → 本插件 on_mail → sink。
//! 另有 `EVENT_PLUG_DOWN_PREFIX` 熔断广播 type 前缀(`ePlugQDown.<name>`),由 worker 自发,
//! 宿主与监控插件均可订阅感知。

use crate::plugin::Plugin;
use std::borrow::Cow;
use std::fmt;
use std::sync::{Arc, Mutex};

use ek_event::Event;

pub const EVENT_LOG: &str = "eLog";
pub const EVENT_PLUG_DOWN_PREFIX: &str = "ePlugQDown.";

/// 对齐 python 约定的级别常量(0..100)。
pub const LOG_LEVEL_INFO: u8 = 20;
pub const LOG_LEVEL_WARN: u8 = 30;
pub const LOG_LEVEL_ERROR: u8 = 40;

/// 日志事件载荷。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LogData {
    pub level: u8,
    pub source: String,
    pub msg: String,
}

impl fmt::Display for LogData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[{}] {}: {}", self.level, self.source, self.msg)
    }
}

/// 日志落点(可换 sink,测试捕获得用)。
pub type LogSink = Arc<dyn Fn(&LogData) + Send + Sync>;

fn default_console_sink() -> LogSink {
    Arc::new(|d: &LogData| println!("{d}"))
}

/// 内置日志插件:注册名 "log",只订阅 EVENT_LOG,deps 为空。
pub struct LogPlugin {
    sink: Mutex<LogSink>,
}

impl LogPlugin {
    pub fn console() -> Self {
        Self { sink: Mutex::new(default_console_sink()) }
    }
    pub fn with_sink(sink: impl Fn(&LogData) + Send + Sync + 'static) -> Self {
        Self { sink: Mutex::new(Arc::new(sink)) }
    }
    pub fn set_sink(&self, sink: LogSink) {
        *self.sink.lock().unwrap() = sink;
    }
}

impl Plugin for LogPlugin {
    fn name(&self) -> &str {
        "log"
    }

    fn routes(&self) -> Vec<Cow<'static, str>> {
        vec![Cow::Borrowed(EVENT_LOG)]
    }

    fn on_mail(&self, event: &Event) {
        if let Some(d) = event.data.downcast_ref::<LogData>() {
            let sink = self.sink.lock().unwrap().clone();
            sink(d);
        }
    }
}