ek-data 0.2.5

Trading data contracts for the enkeep ek ecosystem: venue enums, zero-dep epoch-nanosecond time, decimal-exact price rounding, vt-symbol convention, bus event dictionary, tick/bar/order/trade/position/account/contract objects and request objects (Python enkeep_trade trader/object.py + constant.py, faithfully ported).
Documentation
//! 业务流测试公共件:真总线捕获 + 轮询等待。仅测试使用。
//!
//! 纪律(生态测试约定):
//! - 轮询观察(wait_until),不用 sleep-断言;单场引擎 create→use→stop;
//! - capture 只 downcast 本流载荷类型,异型事件静默忽略(与宿主订阅语义一致);
//! - 键一律走 [`ek_data::event`] 字典 + 派生方法组合(宿主 routes() 的实况形态)。

#![allow(dead_code)]

use ek_event::{Event, EventEngine, HandlerId};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};

/// 类型化捕获槽:注册到一条键上,收集该键上全部下转型成功的载荷。
pub struct Capture<T> {
    inner: Arc<Mutex<Vec<T>>>,
}

impl<T: Clone + Send + Sync + 'static> Capture<T> {
    pub fn new() -> Self {
        Self {
            inner: Arc::new(Mutex::new(Vec::new())),
        }
    }

    /// 把捕获闭包装到引擎指定键上(返回 HandlerId 供注销)。
    pub fn register_on(&self, eng: &EventEngine, key: impl Into<String>) -> HandlerId {
        let inner = Arc::clone(&self.inner);
        eng.register(
            key.into(),
            Arc::new(move |e: &Event| {
                if let Some(d) = e.data.downcast_ref::<T>() {
                    inner.lock().unwrap().push(d.clone());
                }
            }),
        )
    }

    pub fn len(&self) -> usize {
        self.inner.lock().unwrap().len()
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn all(&self) -> Vec<T> {
        self.inner.lock().unwrap().clone()
    }

    pub fn last(&self) -> Option<T> {
        self.inner.lock().unwrap().last().cloned()
    }

    /// 清空后返回(用于分幕时间轴;不注销键)。
    pub fn drain(&self) -> Vec<T> {
        std::mem::take(&mut *self.inner.lock().unwrap())
    }
}

/// 轮询等待:deadline 内断言成立即成功;默认步长 5ms。
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()
}

/// FIFO 排空哨兵:注 n 个哨兵事件并等齐 —— 单分发线程按序处理,
/// 哨兵到齐 ⇒ 之前 put 的全部事件均已分发完毕(负面断言的"窗口已关"证据)。
pub fn drained(eng: &EventEngine, n: usize) -> Capture<i32> {
    let s = Capture::<i32>::new();
    s.register_on(eng, "eTest.settle.");
    for i in 0..n {
        eng.put(Event::with_data("eTest.settle.", Arc::new(i as i32)))
            .unwrap();
    }
    s
}

/// 场景钟:固定 civil 时刻(不与真实时钟耦合,断言可复现)。
pub mod fixture {
    use ek_data::{DtNs, parse_civil};

    /// 场景基准时刻:2024-12-25 09:30:01.500000000(周一;各流 fixture 共用)。
    pub fn t0() -> DtNs {
        parse_civil("2024-12-25 09:30:01.500000000").expect("fixed scene time")
    }
}