Skip to main content

relux_runtime/observe/structured/builder/
values.rs

1//! Variable / interpolation / pure-eval event emitters.
2//!
3//! These cover both the shell-bound surface (`var-let` / `var-assign`
4//! during shell-block execution, runtime interpolation, runtime string
5//! eval) and the pure surface used by `LogSink` and marker replay
6//! (interpolation (shell and pure surfaces), `var-read`, `pure-match`).
7
8use std::collections::HashMap;
9
10use relux_core::diagnostics::IrSpan;
11
12use super::StructuredLogBuilder;
13use crate::observe::structured::event::EventKind;
14use crate::observe::structured::span::SpanId;
15
16impl StructuredLogBuilder {
17    pub fn emit_var_let(
18        &self,
19        span: SpanId,
20        shell: Option<&str>,
21        marker: Option<&str>,
22        name: &str,
23        value: &str,
24        location: Option<&IrSpan>,
25    ) {
26        self.push_event(
27            span,
28            shell,
29            marker,
30            location,
31            EventKind::VarLet {
32                name: name.to_string(),
33                value: value.to_string(),
34            },
35        );
36    }
37
38    #[allow(clippy::too_many_arguments)]
39    pub fn emit_var_assign(
40        &self,
41        span: SpanId,
42        shell: &str,
43        marker: &str,
44        name: &str,
45        value: &str,
46        previous: &str,
47        location: Option<&IrSpan>,
48    ) {
49        self.push_event(
50            span,
51            Some(shell),
52            Some(marker),
53            location,
54            EventKind::VarAssign {
55                name: name.to_string(),
56                value: value.to_string(),
57                previous: previous.to_string(),
58            },
59        );
60    }
61
62    pub fn emit_string_eval(
63        &self,
64        span: SpanId,
65        shell: &str,
66        marker: &str,
67        result: &str,
68        location: Option<&IrSpan>,
69    ) {
70        self.push_event(
71            span,
72            Some(shell),
73            Some(marker),
74            location,
75            EventKind::StringEval {
76                result: result.to_string(),
77            },
78        );
79    }
80
81    /// Emit an interpolation event. Shell callers pass `Some(shell)` /
82    /// `Some(marker)`; pure callers (LogSink) pass `None` / `None`.
83    #[allow(clippy::too_many_arguments)]
84    pub fn emit_interpolation(
85        &self,
86        span: SpanId,
87        shell: Option<&str>,
88        marker: Option<&str>,
89        template: &str,
90        result: &str,
91        bindings: &[(String, String)],
92        location: Option<&IrSpan>,
93    ) {
94        self.push_event(
95            span,
96            shell,
97            marker,
98            location,
99            EventKind::Interpolation {
100                template: template.to_string(),
101                result: result.to_string(),
102                bindings: bindings.to_vec(),
103            },
104        );
105    }
106
107    /// Pure variable-read event. Used by `LogSink` to surface bare
108    /// `${X}`-style reads that resolve against scope/env. The result
109    /// is the resolved string (`""` when the var is undefined).
110    pub fn emit_var_read(&self, span: SpanId, name: &str, value: &str, location: Option<&IrSpan>) {
111        self.push_event(
112            span,
113            None,
114            None,
115            location,
116            EventKind::VarRead {
117                name: name.to_string(),
118                value: value.to_string(),
119            },
120        );
121    }
122
123    /// Pure string-match attempt. Emitted before a marker `?`/`=` or a
124    /// pure-match statement runs. Shell-bound callers (a pure-match statement
125    /// inside a `shell` block) pass `Some(shell)` / `Some(marker)`; shell-less
126    /// callers (marker replay, test/effect preambles) pass `None` / `None`.
127    #[allow(clippy::too_many_arguments)]
128    pub fn emit_pure_match_start(
129        &self,
130        span: SpanId,
131        shell: Option<&str>,
132        marker: Option<&str>,
133        value: &str,
134        pattern: &str,
135        is_regex: bool,
136        location: Option<&IrSpan>,
137    ) {
138        self.push_event(
139            span,
140            shell,
141            marker,
142            location,
143            EventKind::PureMatchStart {
144                value: value.to_string(),
145                pattern: pattern.to_string(),
146                is_regex,
147            },
148        );
149    }
150
151    /// Pure string-match success. Shell context is threaded the same way as
152    /// [`Self::emit_pure_match_start`].
153    pub fn emit_pure_match_done(
154        &self,
155        span: SpanId,
156        shell: Option<&str>,
157        marker: Option<&str>,
158        matched: &str,
159        captures: &HashMap<String, String>,
160        location: Option<&IrSpan>,
161    ) {
162        self.push_event(
163            span,
164            shell,
165            marker,
166            location,
167            EventKind::PureMatchDone {
168                matched: matched.to_string(),
169                captures: captures.clone(),
170            },
171        );
172    }
173
174    /// Pure string-match failure (no match). Shell context is threaded the same
175    /// way as [`Self::emit_pure_match_start`].
176    pub fn emit_pure_match_failed(
177        &self,
178        span: SpanId,
179        shell: Option<&str>,
180        marker: Option<&str>,
181        location: Option<&IrSpan>,
182    ) {
183        self.push_event(span, shell, marker, location, EventKind::PureMatchFailed {});
184    }
185}