metrale_scheduler/
trace_device.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-25: [`TracingDeviceIo`], a decorator that writes one line per launch,
4//! effect, LoRA command and teardown into a sink, then forwards the call to
5//! the inner [`DeviceIo`].
6//!
7//! Owner: scheduler.
8//! Invariants: none beyond the types.
9
10use crate::device::DeviceIo;
11use crate::effect::{Effect, EffectOutcome};
12use crate::plan::{Label, StepPlan, StepResult};
13use crate::types::{DeviceError, DeviceFacts, Ticket};
14
15pub type TraceSink = std::sync::Arc<dyn Fn(String) + Send + Sync>;
16
17pub struct TracingDeviceIo<D: DeviceIo> {
18    inner: D,
19    sink: TraceSink,
20}
21
22impl<D: DeviceIo> TracingDeviceIo<D> {
23    pub fn new(inner: D, sink: TraceSink) -> Self {
24        Self { inner, sink }
25    }
26}
27
28impl<D: DeviceIo> DeviceIo for TracingDeviceIo<D>
29where
30    D::LoraCmd: Label,
31{
32    type Seq = D::Seq;
33    type Ptr = D::Ptr;
34    type Model = D::Model;
35    type LoraCmd = D::LoraCmd;
36    type LoraAck = D::LoraAck;
37
38    fn model(&self) -> &D::Model {
39        self.inner.model()
40    }
41    fn facts(&self) -> DeviceFacts {
42        self.inner.facts()
43    }
44    fn max_depth(&self) -> usize {
45        self.inner.max_depth()
46    }
47    fn launch(&self, plan: StepPlan<'_>, rows: &mut [&mut D::Seq]) -> Result<Ticket, DeviceError> {
48        (self.sink)(format!("fx launch {}", plan.label()));
49        self.inner.launch(plan, rows)
50    }
51    fn await_result(&self, ticket: Ticket) -> Result<StepResult<D::Ptr>, DeviceError> {
52        self.inner.await_result(ticket)
53    }
54    fn apply(&self, effect: Effect<'_, D::Seq, D::Ptr>) -> Result<EffectOutcome, DeviceError> {
55        (self.sink)(format!("fx apply {}", effect.label()));
56        self.inner.apply(effect)
57    }
58    fn lora(&mut self, cmd: D::LoraCmd) -> Result<D::LoraAck, String> {
59        (self.sink)(format!("fx lora {}", cmd.label()));
60        self.inner.lora(cmd)
61    }
62    fn teardown(self: Box<Self>) -> anyhow::Result<()> {
63        (self.sink)("fx teardown".into());
64        Box::new(self.inner).teardown()
65    }
66}