nmbrs_runtime/wrappers/
readout.rs1use std::sync::Arc;
19
20use crate::adapter::{ExecutionError, OpDispenser, OpResult, WrappingDispenser};
21use crate::wrapper_registry::{WrapperName, WrapperRegistration, WrapperSubject};
22
23pub const NAME: WrapperName = WrapperName::new("readout");
25
26fn triggers(s: WrapperSubject) -> bool {
29 let Some(template) = s.op() else {
30 return false;
31 };
32 template
33 .params
34 .get("readout")
35 .and_then(|v| v.as_str())
36 .map(|v| v.eq_ignore_ascii_case("visible"))
37 .unwrap_or(false)
38}
39
40fn describe_assignment(s: WrapperSubject) -> Option<String> {
41 let template = s.op()?;
42 let v = template.params.get("readout")?.as_str()?;
43 Some(format!("readout: {v} (op-level status line)"))
44}
45
46inventory::submit! {
47 WrapperRegistration {
48 name: NAME,
49 owned_fields: &["readout"],
50 triggers,
51 requires_inner: &[super::traverse::NAME],
54 forbids_outer: &[],
55 mutually_exclusive_with: &[],
56 describe_assignment,
57 levels: &[crate::wrapper_registry::WrapperLevel::Op],
58 }
59}
60
61pub struct ReadoutDispenser {
65 inner: Arc<dyn OpDispenser>,
66 op_name: String,
67 measure: Option<String>,
71}
72
73impl ReadoutDispenser {
74 pub fn wrap(inner: Arc<dyn OpDispenser>, op_name: String) -> Arc<dyn OpDispenser> {
75 Self::wrap_with_measure(inner, op_name, None)
76 }
77
78 pub fn wrap_with_measure(
79 inner: Arc<dyn OpDispenser>,
80 op_name: String,
81 measure: Option<String>,
82 ) -> Arc<dyn OpDispenser> {
83 Arc::new(Self {
84 inner,
85 op_name,
86 measure,
87 })
88 }
89}
90
91impl WrappingDispenser for ReadoutDispenser {}
92
93impl OpDispenser for ReadoutDispenser {
94 fn execute<'a>(
95 &'a self,
96 cycle: u64,
97 ctx: &'a crate::fixture::ExecCtx<'a>,
98 ) -> std::pin::Pin<
99 Box<dyn std::future::Future<Output = Result<OpResult, ExecutionError>> + Send + 'a>,
100 > {
101 Box::pin(async move {
102 let parent = crate::execution_context::current_phase_node();
103 let obs = crate::observer::global_observer();
104 if let (Some(p), Some(o)) = (parent, obs.as_ref()) {
105 o.op_starting(p, &self.op_name);
106 }
107 let start = std::time::Instant::now();
108 let result = self.inner.execute(cycle, ctx).await;
109 let dur = start.elapsed().as_secs_f64();
110 if let (Some(p), Some(o)) = (parent, obs.as_ref()) {
111 if let Some(t) = self.measure.as_deref() {
115 match crate::wires::substitute_via_wires(t, ctx.wires) {
116 Ok(text) => o.op_measure(p, &self.op_name, &text),
117 Err(e) => crate::diag!(
118 crate::observer::LogLevel::Debug,
119 "measure: substitution failed for '{t}': {e}"
120 ),
121 }
122 }
123 match &result {
124 Ok(_) => o.op_completed(p, &self.op_name, dur),
125 Err(e) => o.op_failed(p, &self.op_name, &format!("{e}")),
126 }
127 }
128 result
129 })
130 }
131
132 fn inner_dispenser(&self) -> Option<&dyn OpDispenser> {
133 Some(self.inner.as_ref())
134 }
135}