use serde::Deserialize;
use serde::Serialize;
use ts_rs::TS;
use super::SourceLocation;
use super::event::EventSeq;
use super::event::TimeoutValue;
use super::span::SpanId;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[cfg_attr(
feature = "ts-export",
ts(export, export_to = "../../../viewer/src/types/")
)]
pub struct StackFrame {
pub span: SpanId,
pub kind: String,
pub name: Option<String>,
pub args: Vec<(String, String)>,
pub alias: Option<String>,
pub location: Option<SourceLocation>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[cfg_attr(
feature = "ts-export",
ts(export, export_to = "../../../viewer/src/types/")
)]
#[serde(tag = "type", rename_all = "kebab-case")]
pub enum FailureRecord {
MatchTimeout {
span: SpanId,
event_seq: EventSeq,
shell: String,
pattern: String,
effective: TimeoutValue,
call_stack: Vec<StackFrame>,
buffer_tail: String,
vars_in_scope: Vec<(String, String)>,
},
FailPatternMatched {
span: SpanId,
event_seq: EventSeq,
shell: String,
pattern: String,
matched_line: String,
call_stack: Vec<StackFrame>,
buffer_tail: String,
vars_in_scope: Vec<(String, String)>,
},
ShellExited {
span: SpanId,
event_seq: EventSeq,
shell: String,
exit_code: Option<i32>,
call_stack: Vec<StackFrame>,
buffer_tail: String,
vars_in_scope: Vec<(String, String)>,
},
Runtime {
span: Option<SpanId>,
event_seq: Option<EventSeq>,
shell: Option<String>,
message: String,
call_stack: Vec<StackFrame>,
vars_in_scope: Vec<(String, String)>,
},
MultiMatch {
span: SpanId,
event_seq: EventSeq,
shell: String,
patterns: Vec<super::event::MultiMatchPattern>,
matched: Vec<usize>,
effective: TimeoutValue,
call_stack: Vec<StackFrame>,
buffer_tail: String,
vars_in_scope: Vec<(String, String)>,
},
}
#[cfg(test)]
mod tests {
use super::*;
use crate::observe::structured::event::MultiMatchPattern;
#[test]
fn failure_record_multimatch_round_trips_serde() {
let original = FailureRecord::MultiMatch {
span: 3,
event_seq: 11,
shell: "default".into(),
patterns: vec![
MultiMatchPattern {
pattern: "^a$".into(),
is_regex: true,
},
MultiMatchPattern {
pattern: "b".into(),
is_regex: false,
},
],
matched: vec![1],
effective: TimeoutValue::Assertion {
duration: "5s".into(),
source: None,
},
call_stack: vec![],
buffer_tail: "tail".into(),
vars_in_scope: vec![("k".into(), "v".into())],
};
let json = serde_json::to_string(&original).unwrap();
assert!(json.contains("\"type\":\"multi-match\""), "json: {json}");
let parsed: FailureRecord = serde_json::from_str(&json).unwrap();
match parsed {
FailureRecord::MultiMatch {
matched, patterns, ..
} => {
assert_eq!(matched, vec![1usize]);
assert_eq!(patterns.len(), 2);
}
other => panic!("expected MultiMatch, got {other:?}"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[cfg_attr(
feature = "ts-export",
ts(export, export_to = "../../../viewer/src/types/")
)]
pub struct CancellationRecord {
pub reason: super::event::CancelReasonRecord,
pub span: Option<SpanId>,
pub event_seq: Option<EventSeq>,
pub shell: Option<String>,
pub call_stack: Vec<StackFrame>,
}