use relux_core::diagnostics::IrSpan;
use super::StructuredLogBuilder;
use crate::observe::progress::ProgressEvent;
use crate::observe::structured::event::CancelReasonRecord;
use crate::observe::structured::event::EventKind;
use crate::observe::structured::failure::CancellationRecord;
use crate::observe::structured::failure::FailureRecord;
use crate::observe::structured::span::SpanId;
impl StructuredLogBuilder {
pub fn emit_annotate(
&self,
span: SpanId,
shell: &str,
marker: &str,
text: &str,
location: Option<&IrSpan>,
) {
self.push_event(
span,
Some(shell),
Some(marker),
location,
EventKind::Annotate {
text: text.to_string(),
},
);
self.push_progress(ProgressEvent::Annotation(text.to_string()));
}
pub fn emit_log(
&self,
span: SpanId,
shell: &str,
marker: &str,
message: &str,
location: Option<&IrSpan>,
) {
self.push_event(
span,
Some(shell),
Some(marker),
location,
EventKind::Log {
message: message.to_string(),
},
);
}
pub fn emit_warning(
&self,
span: SpanId,
shell: &str,
marker: &str,
message: &str,
location: Option<&IrSpan>,
) {
self.push_event(
span,
Some(shell),
Some(marker),
location,
EventKind::Warning {
message: message.to_string(),
},
);
self.push_progress(ProgressEvent::Warning(message.to_string()));
}
pub fn emit_error(
&self,
span: SpanId,
shell: &str,
marker: &str,
message: &str,
location: Option<&IrSpan>,
) {
self.push_event(
span,
Some(shell),
Some(marker),
location,
EventKind::Error {
message: message.to_string(),
},
);
self.push_progress(ProgressEvent::Error(message.to_string()));
}
pub fn emit_cancelled(
&self,
span: SpanId,
shell: Option<&str>,
shell_marker: Option<&str>,
reason: &crate::cancel::CancelReason,
) {
self.push_event(
span,
shell,
shell_marker,
None,
EventKind::Cancelled {
reason: CancelReasonRecord::from(reason),
},
);
self.push_progress(ProgressEvent::Cancellation);
}
pub fn emit_failure_progress(&self) {
self.push_progress(ProgressEvent::Failure);
}
pub fn failure_record(&self, failure: &crate::report::result::Failure) -> FailureRecord {
use crate::report::result::Failure;
match failure {
Failure::MatchTimeout {
pattern,
shell,
effective,
context,
..
} => FailureRecord::MatchTimeout {
span: context.span().unwrap_or(0),
event_seq: context.event_seq().unwrap_or(0),
shell: shell.clone(),
pattern: pattern.clone(),
effective: self.timeout_value(effective),
call_stack: context.call_stack().to_vec(),
buffer_tail: context.buffer_tail().to_string(),
vars_in_scope: context.vars_in_scope().to_vec(),
},
Failure::FailPatternMatched {
pattern,
matched_line,
shell,
context,
..
} => FailureRecord::FailPatternMatched {
span: context.span().unwrap_or(0),
event_seq: context.event_seq().unwrap_or(0),
shell: shell.clone(),
pattern: pattern.clone(),
matched_line: matched_line.clone(),
call_stack: context.call_stack().to_vec(),
buffer_tail: context.buffer_tail().to_string(),
vars_in_scope: context.vars_in_scope().to_vec(),
},
Failure::ShellExited {
shell,
exit_code,
context,
..
} => FailureRecord::ShellExited {
span: context.span().unwrap_or(0),
event_seq: context.event_seq().unwrap_or(0),
shell: shell.clone(),
exit_code: *exit_code,
call_stack: context.call_stack().to_vec(),
buffer_tail: context.buffer_tail().to_string(),
vars_in_scope: context.vars_in_scope().to_vec(),
},
Failure::Runtime {
message,
shell,
context,
..
} => FailureRecord::Runtime {
span: context.span(),
event_seq: context.event_seq(),
shell: shell.clone(),
message: message.clone(),
call_stack: context.call_stack().to_vec(),
vars_in_scope: context.vars_in_scope().to_vec(),
},
Failure::PureMatch {
value,
pattern,
is_regex,
match_context,
context,
..
} => FailureRecord::PureMatch {
span: context
.span()
.expect("pure-match failure always carries a span"),
event_seq: context
.event_seq()
.expect("pure-match failure always carries an event seq"),
match_context: match_context.clone(),
value: value.clone(),
pattern: pattern.clone(),
is_regex: *is_regex,
call_stack: context.call_stack().to_vec(),
vars_in_scope: context.vars_in_scope().to_vec(),
},
Failure::MultiMatch {
shell,
patterns,
matched,
effective,
context,
..
} => FailureRecord::MultiMatch {
span: context.span().unwrap_or(0),
event_seq: context.event_seq().unwrap_or(0),
shell: shell.clone(),
patterns: patterns.clone(),
matched: matched.clone(),
effective: self.timeout_value(effective),
call_stack: context.call_stack().to_vec(),
buffer_tail: context.buffer_tail().to_string(),
vars_in_scope: context.vars_in_scope().to_vec(),
},
}
}
pub fn cancellation_record(
&self,
c: &crate::report::result::Cancellation,
) -> CancellationRecord {
let ctx = &c.context;
CancellationRecord {
reason: CancelReasonRecord::from(&c.reason),
span: ctx.span(),
event_seq: ctx.event_seq(),
shell: None,
call_stack: ctx.call_stack().to_vec(),
}
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Instant;
use super::StructuredLogBuilder;
use crate::observe::progress;
use crate::observe::structured::MatchContext;
use crate::observe::structured::failure::FailureRecord;
use crate::report::result::Failure;
use crate::report::result::FailureContext;
use relux_core::diagnostics::IrSpan;
fn make_builder() -> StructuredLogBuilder {
let (tx, _rx) = progress::channel();
let sources = relux_core::table::SharedTable::new();
StructuredLogBuilder::new(
tx,
Instant::now(),
sources,
Arc::from(PathBuf::from("/project").as_path()),
)
}
#[test]
fn pure_match_record_carries_real_seq_and_vars() {
let builder = make_builder();
let f = Failure::PureMatch {
value: "abc".into(),
pattern: "xyz".into(),
is_regex: false,
span: IrSpan::synthetic(),
match_context: MatchContext::TestPreamble {
name: "login".into(),
},
context: FailureContext::pure(7, 3, vec![], vec![("v".into(), "abc".into())]),
};
match builder.failure_record(&f) {
FailureRecord::PureMatch {
span,
event_seq,
match_context,
vars_in_scope,
value,
pattern,
..
} => {
assert_eq!(span, 7);
assert_eq!(event_seq, 3, "real seq is threaded through, not 0");
assert_eq!(
match_context,
MatchContext::TestPreamble {
name: "login".into()
}
);
assert_eq!(vars_in_scope, vec![("v".to_string(), "abc".to_string())]);
assert_eq!(value, "abc");
assert_eq!(pattern, "xyz");
}
other => panic!("expected PureMatch, got {other:?}"),
}
}
}