use relux_core::diagnostics::IrSpan;
use super::SpanGuard;
use super::StructuredLogBuilder;
use crate::observe::progress::ProgressEvent;
use crate::observe::structured::event::EventKind;
use crate::observe::structured::event::EventSeq;
use crate::observe::structured::failure::StackFrame;
use crate::observe::structured::shell::ShellRecord;
use crate::observe::structured::span::Span;
use crate::observe::structured::span::SpanId;
use crate::observe::structured::span::SpanKind;
impl StructuredLogBuilder {
pub fn open_span(
&self,
kind: SpanKind,
parent: Option<SpanId>,
location: Option<&IrSpan>,
) -> SpanGuard {
let location = location.and_then(|s| self.resolve_location(s));
let start_ts = self.now();
let id = {
let mut inner = self.inner.lock().unwrap();
let id = inner.next_span_id;
inner.next_span_id += 1;
inner.spans.insert(
id,
Span {
id,
kind,
parent,
start_ts,
end_ts: None,
location,
},
);
id
};
SpanGuard::new(id, self.clone())
}
pub(super) fn close_span_inner(&self, id: SpanId) {
let end_ts = self.now();
let mut inner = self.inner.lock().unwrap();
if let Some(span) = inner.spans.get_mut(&id)
&& span.end_ts.is_none()
{
span.end_ts = Some(end_ts);
}
}
pub fn close_span(&self, id: SpanId) {
self.close_span_inner(id);
}
pub fn set_fn_call_result(&self, id: SpanId, result: &str) {
let mut inner = self.inner.lock().unwrap();
if let Some(span) = inner.spans.get_mut(&id)
&& let SpanKind::FnCall { result: slot, .. } = &mut span.kind
{
*slot = Some(result.to_string());
}
}
pub fn resolve_stack(&self, leaf: SpanId) -> Vec<StackFrame> {
let inner = self.inner.lock().unwrap();
let mut chain: Vec<StackFrame> = Vec::new();
let mut next = Some(leaf);
while let Some(id) = next {
let Some(span) = inner.spans.get(&id) else {
break;
};
let (name, args) = span.kind.frame_data();
chain.push(StackFrame {
span: id,
kind: span.kind.kind_str().to_string(),
name,
args,
alias: span.kind.frame_alias(),
location: span.location.clone(),
});
next = span.parent;
}
chain.reverse();
chain
}
pub fn open_markers_span(&self, location: Option<&IrSpan>) -> SpanGuard {
self.open_span(SpanKind::Markers, None, location)
}
pub fn open_marker_eval_span(
&self,
parent: SpanId,
marker_kind: super::super::span::MarkerEvalKind,
modifier: super::super::span::MarkerEvalModifier,
decision: super::super::span::MarkerEvalDecision,
location: Option<&IrSpan>,
) -> SpanGuard {
self.open_span(
SpanKind::MarkerEval {
marker_kind,
modifier,
decision,
},
Some(parent),
location,
)
}
pub fn emit_bool_check(
&self,
span: SpanId,
evaluation: super::super::span::MarkerEvalDetail,
location: Option<&IrSpan>,
) -> EventSeq {
self.push_event(
span,
None,
None,
location,
EventKind::BoolCheck { evaluation },
)
}
pub fn record_shell_spawn(&self, marker: &str, name: &str, command: &str) {
let spawn_ts = self.now();
let mut inner = self.inner.lock().unwrap();
inner.shells.insert(
marker.to_string(),
ShellRecord {
marker: marker.to_string(),
name: name.to_string(),
spawn_ts,
terminate_ts: None,
command: command.to_string(),
},
);
}
pub fn record_shell_terminate(&self, marker: &str) {
let terminate_ts = self.now();
let mut inner = self.inner.lock().unwrap();
if let Some(rec) = inner.shells.get_mut(marker) {
rec.terminate_ts = Some(terminate_ts);
}
}
pub fn emit_shell_spawn(
&self,
span: SpanId,
shell: &str,
marker: &str,
command: &str,
location: Option<&IrSpan>,
) {
self.record_shell_spawn(marker, shell, command);
self.push_event(
span,
Some(shell),
Some(marker),
location,
EventKind::ShellSpawn {
name: shell.to_string(),
command: command.to_string(),
},
);
self.push_progress(ProgressEvent::ShellSpawn);
}
pub fn emit_shell_ready(
&self,
span: SpanId,
shell: &str,
marker: &str,
location: Option<&IrSpan>,
) {
self.push_event(
span,
Some(shell),
Some(marker),
location,
EventKind::ShellReady {
name: shell.to_string(),
},
);
}
pub fn emit_shell_switch(
&self,
span: SpanId,
shell: &str,
marker: &str,
location: Option<&IrSpan>,
) {
self.push_event(
span,
Some(shell),
Some(marker),
location,
EventKind::ShellSwitch {
name: shell.to_string(),
},
);
self.push_progress(ProgressEvent::ShellSwitch(shell.to_string()));
}
pub fn emit_shell_terminate(
&self,
span: SpanId,
shell: &str,
marker: &str,
location: Option<&IrSpan>,
) {
self.record_shell_terminate(marker);
self.push_event(
span,
Some(shell),
Some(marker),
location,
EventKind::ShellTerminate {
name: shell.to_string(),
},
);
self.push_progress(ProgressEvent::ShellTerminate);
}
pub fn push_fn_enter(&self, name: &str) {
self.push_progress(ProgressEvent::FnEnter(name.to_string()));
}
pub fn push_fn_exit(&self) {
self.push_progress(ProgressEvent::FnExit);
}
pub fn push_effect_setup(&self, name: &str) {
self.push_progress(ProgressEvent::EffectSetup(name.to_string()));
}
pub fn push_effect_teardown(&self) {
self.push_progress(ProgressEvent::EffectTeardown);
}
pub fn emit_effect_expose_shell(
&self,
span: SpanId,
name: &str,
target: &str,
qualifier: Option<&str>,
location: Option<&IrSpan>,
) {
self.push_event(
span,
None,
None,
location,
EventKind::EffectExposeShell {
name: name.to_string(),
target: target.to_string(),
qualifier: qualifier.map(String::from),
},
);
}
pub fn emit_effect_expose_var(
&self,
span: SpanId,
name: &str,
target: &str,
qualifier: Option<&str>,
value: &str,
location: Option<&IrSpan>,
) {
self.push_event(
span,
None,
None,
location,
EventKind::EffectExposeVar {
name: name.to_string(),
target: target.to_string(),
qualifier: qualifier.map(String::from),
value: value.to_string(),
},
);
}
}