Skip to main content

relux_runtime/observe/structured/builder/
matching.rs

1//! Match, timeout, fail-pattern, and timeout-config emitters.
2//!
3//! Anything that participates in the "wait for output" half of the
4//! send/match contract: arming/disarming fail patterns, opening a
5//! match attempt, recording its resolution (matched or timed out), and
6//! pushing a `TimeoutSet` event whenever the active match timeout changes.
7
8use std::collections::HashMap;
9use std::time::Duration;
10
11use relux_core::diagnostics::IrSpan;
12use relux_ir::IrTimeout;
13
14use super::StructuredLogBuilder;
15use crate::observe::progress::ProgressEvent;
16use crate::observe::structured::event::EventKind;
17use crate::observe::structured::event::EventSeq;
18use crate::observe::structured::span::SpanId;
19
20impl StructuredLogBuilder {
21    #[allow(clippy::too_many_arguments)]
22    pub fn emit_match_start(
23        &self,
24        span: SpanId,
25        shell: &str,
26        marker: &str,
27        pattern: &str,
28        is_regex: bool,
29        effective: &IrTimeout,
30        location: Option<&IrSpan>,
31    ) {
32        let effective = self.timeout_value(effective);
33        self.push_event(
34            span,
35            Some(shell),
36            Some(marker),
37            location,
38            EventKind::MatchStart {
39                pattern: pattern.to_string(),
40                is_regex,
41                effective,
42            },
43        );
44        self.push_progress(ProgressEvent::MatchStart);
45    }
46
47    /// Record a structured `MatchDone` event referencing a buffer event
48    /// that was pushed (atomically with the consume operation) by
49    /// `OutputBuffer::consume_*`. The buffer event push is the consumer's
50    /// responsibility - this method only emits the structured event +
51    /// progress notification.
52    #[allow(clippy::too_many_arguments)]
53    pub fn emit_match_done_record(
54        &self,
55        span: SpanId,
56        shell: &str,
57        marker: &str,
58        matched: &str,
59        elapsed: Duration,
60        captures: Option<HashMap<String, String>>,
61        buffer_seq: EventSeq,
62        location: Option<&IrSpan>,
63    ) {
64        self.push_event(
65            span,
66            Some(shell),
67            Some(marker),
68            location,
69            EventKind::MatchDone {
70                matched: matched.to_string(),
71                elapsed,
72                captures,
73                buffer_seq,
74            },
75        );
76        self.push_progress(ProgressEvent::MatchDone);
77    }
78
79    pub fn emit_timeout(
80        &self,
81        span: SpanId,
82        shell: &str,
83        marker: &str,
84        pattern: &str,
85        effective: &IrTimeout,
86        location: Option<&IrSpan>,
87    ) {
88        let effective = self.timeout_value(effective);
89        self.push_event(
90            span,
91            Some(shell),
92            Some(marker),
93            location,
94            EventKind::Timeout {
95                pattern: pattern.to_string(),
96                buffer_seq: None,
97                effective,
98            },
99        );
100        self.push_progress(ProgressEvent::Timeout);
101    }
102
103    pub fn emit_fail_pattern_set(
104        &self,
105        span: SpanId,
106        shell: &str,
107        marker: &str,
108        pattern: &str,
109        is_regex: bool,
110        location: Option<&IrSpan>,
111    ) {
112        self.push_event(
113            span,
114            Some(shell),
115            Some(marker),
116            location,
117            EventKind::FailPatternSet {
118                pattern: pattern.to_string(),
119                is_regex,
120            },
121        );
122    }
123
124    pub fn emit_fail_pattern_cleared(
125        &self,
126        span: SpanId,
127        shell: &str,
128        marker: &str,
129        location: Option<&IrSpan>,
130    ) {
131        self.push_event(
132            span,
133            Some(shell),
134            Some(marker),
135            location,
136            EventKind::FailPatternCleared,
137        );
138    }
139
140    #[allow(clippy::too_many_arguments)]
141    pub fn emit_fail_pattern_triggered(
142        &self,
143        span: SpanId,
144        shell: &str,
145        marker: &str,
146        pattern: &str,
147        is_regex: bool,
148        matched_line: &str,
149        location: Option<&IrSpan>,
150    ) {
151        self.push_event(
152            span,
153            Some(shell),
154            Some(marker),
155            location,
156            EventKind::FailPatternTriggered {
157                pattern: pattern.to_string(),
158                is_regex,
159                matched_line: matched_line.to_string(),
160                buffer_seq: None,
161            },
162        );
163        self.push_progress(ProgressEvent::FailPattern);
164    }
165
166    pub fn emit_timeout_set(
167        &self,
168        span: SpanId,
169        shell: &str,
170        marker: &str,
171        timeout: &IrTimeout,
172        previous: &IrTimeout,
173        location: Option<&IrSpan>,
174    ) {
175        let timeout = self.timeout_value(timeout);
176        let previous = self.timeout_value(previous);
177        self.push_event(
178            span,
179            Some(shell),
180            Some(marker),
181            location,
182            EventKind::TimeoutSet { timeout, previous },
183        );
184    }
185
186    /// Emit the `multi-match-start` event for `<{ ... }`. `patterns` is in
187    /// source order; the runtime will reference these indices from later
188    /// `multi-match-pattern-done` and `multi-match-timeout` events.
189    #[allow(clippy::too_many_arguments)]
190    pub fn emit_multimatch_start(
191        &self,
192        span: SpanId,
193        shell: &str,
194        marker: &str,
195        patterns: &[crate::observe::structured::event::MultiMatchPattern],
196        effective: &IrTimeout,
197        location: Option<&IrSpan>,
198    ) {
199        let effective = self.timeout_value(effective);
200        self.push_event(
201            span,
202            Some(shell),
203            Some(marker),
204            location,
205            EventKind::MultiMatchStart {
206                effective,
207                patterns: patterns.to_vec(),
208            },
209        );
210    }
211
212    /// Emit a `multi-match-pattern-done` event for a single pattern within
213    /// an in-flight multimatch block. `buffer_seq` references the
214    /// `BufferEventKind::Matched` event pushed atomically with the
215    /// pattern's success under the buf lock; that event carries the
216    /// matched text and offsets (single source of truth).
217    #[allow(clippy::too_many_arguments)]
218    pub fn emit_multimatch_pattern_done(
219        &self,
220        span: SpanId,
221        shell: &str,
222        marker: &str,
223        index: usize,
224        elapsed: Duration,
225        buffer_seq: EventSeq,
226        location: Option<&IrSpan>,
227    ) {
228        self.push_event(
229            span,
230            Some(shell),
231            Some(marker),
232            location,
233            EventKind::MultiMatchPatternDone {
234                index,
235                elapsed,
236                buffer_seq,
237            },
238        );
239    }
240
241    /// Emit a `multi-match-done` event - the block satisfied every
242    /// pattern. `advance_to` references the per-pattern `Matched` buffer
243    /// event whose match ends farthest in the buffer (the single drain
244    /// the runtime applies at block exit advances the cursor by
245    /// `len(before) + len(matched)` of that referenced event).
246    pub fn emit_multimatch_done(
247        &self,
248        span: SpanId,
249        shell: &str,
250        marker: &str,
251        advance_to: EventSeq,
252        location: Option<&IrSpan>,
253    ) {
254        self.push_event(
255            span,
256            Some(shell),
257            Some(marker),
258            location,
259            EventKind::MultiMatchDone { advance_to },
260        );
261    }
262
263    /// Emit a `multi-match-timeout` event - the block deadline expired
264    /// with at least one pattern still unmatched. `unmatched` is a list
265    /// of indices into the original `MultiMatchStart.patterns` vec.
266    pub fn emit_multimatch_timeout(
267        &self,
268        span: SpanId,
269        shell: &str,
270        marker: &str,
271        unmatched: &[usize],
272        location: Option<&IrSpan>,
273    ) {
274        self.push_event(
275            span,
276            Some(shell),
277            Some(marker),
278            location,
279            EventKind::MultiMatchTimeout {
280                unmatched: unmatched.to_vec(),
281            },
282        );
283        self.push_progress(ProgressEvent::Timeout);
284    }
285}