⎇ 53dbdf0f1deea10d6fc567ea2b43e1e75d6d60cd
crates/core/ente-brain/src/dispatch.rs
History
Who touched what
raw
chore: monorepo inicial con arje + minga + yahweh absorbidos
1//! Despacho asíncrono de Actions. El motor entrega `Vec<Arc<Rule>>` matched;
2//! este módulo las traduce a efectos del fractal vía un `ActionSink` trait.
3//!
4//! Esto invierte la dependencia: ente-brain no conoce a ente-zero. El init
5//! implementa `ActionSink` y wirea spawn/invoke/log a sus propias estructuras.
6
7use crate::rules::{Action, LogLevel, Rule};
8use std::sync::Arc;
9use tracing::{debug, error, info, trace, warn};
10
11/// Backend de ejecución de Actions. ente-zero implementa esto delegando a
12/// graph_tx (Spawn → SpawnRequest, Invoke → bus call, etc.).
13pub trait ActionSink: Send + Sync {
14 /// Spawn una Card decodificada. Implementación: GraphEvent::SpawnRequest.
15 fn spawn(&self, card_blob: &str);
16 /// Invoke por bus. blob crudo; el sink lo enruta vía bus_mediator.
17 fn invoke(&self, target_cap: ente_card::Capability, blob: Vec<u8>);
18 /// Notifica a un Ente específico (target_id). Implementación: forward por bus.
19 fn notify(&self, target_id: ulid::Ulid, message: &str);
20 /// Inhibe un comportamiento (placeholder; semántica depende del sink).
21 fn inhibit(&self, reason: &str);
22}
23
24/// Sink por defecto que sólo logea. Útil para tests y dev sin runtime.
25pub struct NullSink;
26
27impl ActionSink for NullSink {
28 fn spawn(&self, card_blob: &str) {
29 info!(blob_len = card_blob.len(), "NullSink::spawn (no-op)");
30 }
31 fn invoke(&self, target_cap: ente_card::Capability, blob: Vec<u8>) {
32 info!(?target_cap, blob_len = blob.len(), "NullSink::invoke (no-op)");
33 }
34 fn notify(&self, target_id: ulid::Ulid, message: &str) {
35 info!(%target_id, %message, "NullSink::notify (no-op)");
36 }
37 fn inhibit(&self, reason: &str) {
38 info!(%reason, "NullSink::inhibit (no-op)");
39 }
40}
41
42/// Ejecuta las reglas matched. Cada Rule puede tener N Actions; ejecutamos
43/// todas. Las acciones de Log se evalúan inline (tracing es async-safe).
44/// Las acciones de Spawn/Invoke/Notify se delegan al sink — el sink decide
45/// si procesarlas sincrónica o asincrónicamente.
46pub async fn dispatch_actions(rules: &[Arc<Rule>], sink: &dyn ActionSink) {
47 for rule in rules {
48 trace!(id = %rule.id, priority = rule.priority, n = rule.then.len(), "dispatching rule");
49 for action in &rule.then {
50 execute_action(action, sink, rule.id).await;
51 }
52 }
53}
54
55async fn execute_action(action: &Action, sink: &dyn ActionSink, rule_id: ulid::Ulid) {
56 match action {
57 Action::Log { level, message } => emit_log(level, message, rule_id),
58 Action::Notify { target_id, message } => sink.notify(*target_id, message),
59 Action::Spawn { card_blob } => sink.spawn(card_blob),
60 Action::Invoke { target_cap, blob } => sink.invoke(target_cap.clone(), blob.clone()),
61 Action::Inhibit { reason } => sink.inhibit(reason),
62 }
63}
64
65fn emit_log(level: &LogLevel, message: &str, rule_id: ulid::Ulid) {
66 match level {
67 LogLevel::Trace => trace!(rule = %rule_id, "{}", message),
68 LogLevel::Debug => debug!(rule = %rule_id, "{}", message),
69 LogLevel::Info => info! (rule = %rule_id, "{}", message),
70 LogLevel::Warn => warn! (rule = %rule_id, "{}", message),
71 LogLevel::Error => error!(rule = %rule_id, "{}", message),
72 }
73}