stepflow_flow/
error_stack.rs1#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
15pub struct ErrorStackEntry {
16 pub error: String,
18 #[serde(skip_serializing_if = "Vec::is_empty", default)]
20 pub attachments: Vec<String>,
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub backtrace: Option<String>,
24}
25
26#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
28pub struct ErrorStack {
29 #[serde(skip_serializing_if = "Vec::is_empty", default)]
30 pub stack: Vec<ErrorStackEntry>,
31}
32
33impl ErrorStack {
34 pub fn from_error_stack<T: error_stack::Context>(report: error_stack::Report<T>) -> Self {
36 let mut stack_entries: Vec<ErrorStackEntry> = Vec::new();
37 let mut current_attachments: Vec<String> = Vec::new();
38
39 let global_backtrace = {
41 let mut backtrace_iter = report.frames().filter_map(|frame| {
42 frame
43 .sources()
44 .iter()
45 .find_map(|source| source.downcast_ref::<std::backtrace::Backtrace>())
46 });
47 backtrace_iter.next().map(|bt| bt.to_string())
48 };
49
50 for frame in report.frames() {
51 match frame.kind() {
52 error_stack::FrameKind::Context(context) => {
53 if !current_attachments.is_empty()
55 && !stack_entries.is_empty()
56 && let Some(last_entry) = stack_entries.last_mut()
57 {
58 last_entry.attachments.append(&mut current_attachments);
59 }
60
61 let backtrace = if stack_entries.is_empty() {
64 global_backtrace.clone()
65 } else {
66 None
67 };
68
69 stack_entries.push(ErrorStackEntry {
70 error: context.to_string(),
71 attachments: vec![],
72 backtrace,
73 });
74 }
75 error_stack::FrameKind::Attachment(attachment_kind) => {
76 if let error_stack::AttachmentKind::Printable(printable) = attachment_kind {
77 current_attachments.push(printable.to_string());
78 }
79 }
80 }
81 }
82
83 if !current_attachments.is_empty()
85 && !stack_entries.is_empty()
86 && let Some(last_entry) = stack_entries.last_mut()
87 {
88 last_entry.attachments.extend(current_attachments);
89 }
90
91 Self {
92 stack: stack_entries,
93 }
94 }
95}
96
97impl<T: error_stack::Context> From<error_stack::Report<T>> for ErrorStack {
98 fn from(report: error_stack::Report<T>) -> Self {
99 Self::from_error_stack(report)
100 }
101}