1use crate::{
2 AuditEntry, ClearReason, CollectionDiffTrace, InvariantResultTrace, OutputFrameKind, OutputKey,
3 RebaselineReason, ResourceCommand, ResourceKey, ScopeId, ScopeLifecycleTrace,
4 StagedInputChange, TransactionId, TransactionPhase, TransactionResult,
5};
6use crate::{NodeId, Revision};
7
8#[derive(Clone, Debug, Eq, PartialEq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct TransactionTrace {
12 pub transaction_id: TransactionId,
14 pub revision: Revision,
16 pub staged_input_changes: Vec<StagedInputChange>,
18 pub changed_inputs: Vec<NodeId>,
20 pub dirty_roots: Vec<NodeId>,
22 pub recomputed_derived_nodes: Vec<NodeId>,
24 pub changed_derived_nodes: Vec<NodeId>,
26 pub recomputed_collection_nodes: Vec<NodeId>,
28 pub changed_collection_nodes: Vec<NodeId>,
30 pub collection_diffs: Vec<CollectionDiffTrace>,
32 pub resource_commands: Vec<ResourceCommandTrace>,
34 pub output_frames: Vec<OutputFrameTrace>,
36 pub scope_events: Vec<ScopeLifecycleTrace>,
38 pub audit_log: Vec<AuditEntry>,
40 pub phase_trace: Vec<TransactionPhase>,
42 pub invariant_results: Vec<InvariantResultTrace>,
44}
45
46impl TransactionTrace {
47 pub fn from_result<C>(result: &TransactionResult<C>) -> Self {
49 Self {
50 transaction_id: result.transaction_id,
51 revision: result.revision,
52 staged_input_changes: result.staged_input_changes.clone(),
53 changed_inputs: result.changed_inputs.clone(),
54 dirty_roots: result.dirty_roots.clone(),
55 recomputed_derived_nodes: result.recomputed_derived_nodes.clone(),
56 changed_derived_nodes: result.changed_derived_nodes.clone(),
57 recomputed_collection_nodes: result.recomputed_collection_nodes.clone(),
58 changed_collection_nodes: result.changed_collection_nodes.clone(),
59 collection_diffs: result.collection_diffs.clone(),
60 resource_commands: result
61 .resource_plan
62 .commands()
63 .iter()
64 .map(ResourceCommandTrace::from_command)
65 .collect(),
66 output_frames: result
67 .output_frames
68 .iter()
69 .map(|frame| OutputFrameTrace {
70 output_key: frame.output_key,
71 scope: frame.scope,
72 transaction_id: frame.transaction_id,
73 revision: frame.revision,
74 kind: OutputFrameKindTrace::from_kind(&frame.kind),
75 })
76 .collect(),
77 scope_events: result.scope_events.clone(),
78 audit_log: result.audit_log.clone(),
79 phase_trace: result.phase_trace.clone(),
80 invariant_results: result.invariant_results.clone(),
81 }
82 }
83}
84
85#[derive(Clone, Debug, Eq, PartialEq)]
87#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88pub struct ResourceCommandTrace {
89 pub key: ResourceKey,
91 pub scope: ScopeId,
93 pub kind: ResourceCommandKind,
95}
96
97impl ResourceCommandTrace {
98 fn from_command<C>(command: &ResourceCommand<C>) -> Self {
99 Self {
100 key: command.key().clone(),
101 scope: command.scope(),
102 kind: ResourceCommandKind::from_command(command),
103 }
104 }
105}
106
107#[derive(Copy, Clone, Debug, Eq, PartialEq)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110pub enum ResourceCommandKind {
111 Open,
113 Close,
115 Replace,
117 Refresh,
119}
120
121impl ResourceCommandKind {
122 pub(crate) fn from_command<C>(command: &ResourceCommand<C>) -> Self {
123 match command {
124 ResourceCommand::Open { .. } => Self::Open,
125 ResourceCommand::Close { .. } => Self::Close,
126 ResourceCommand::Replace { .. } => Self::Replace,
127 ResourceCommand::Refresh { .. } => Self::Refresh,
128 }
129 }
130}
131
132#[derive(Clone, Debug, Eq, PartialEq)]
134#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
135pub struct OutputFrameTrace {
136 pub output_key: OutputKey,
138 pub scope: ScopeId,
140 pub transaction_id: TransactionId,
142 pub revision: Revision,
144 pub kind: OutputFrameKindTrace,
146}
147
148#[derive(Copy, Clone, Debug, Eq, PartialEq)]
150#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
151pub enum OutputFrameKindTrace {
152 Baseline,
154 Delta,
156 Clear(ClearReason),
158 Rebaseline(RebaselineReason),
160}
161
162impl OutputFrameKindTrace {
163 pub(crate) fn from_kind(kind: &OutputFrameKind) -> Self {
164 match kind {
165 OutputFrameKind::Baseline(_) => Self::Baseline,
166 OutputFrameKind::Delta(_) => Self::Delta,
167 OutputFrameKind::Clear(reason) => Self::Clear(*reason),
168 OutputFrameKind::Rebaseline(_, reason) => Self::Rebaseline(*reason),
169 }
170 }
171}
172
173impl<C> TransactionResult<C> {
174 pub fn trace(&self) -> TransactionTrace {
176 TransactionTrace::from_result(self)
177 }
178}
179
180#[derive(Clone, Debug, Eq, PartialEq)]
182#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
183pub struct TraceMismatch {
184 pub expected: Vec<TransactionTrace>,
186 pub actual: Vec<TransactionTrace>,
188}
189
190pub fn assert_transaction_traces_match(
192 expected: &[TransactionTrace],
193 actual: &[TransactionTrace],
194) -> Result<(), TraceMismatch> {
195 if expected == actual {
196 Ok(())
197 } else {
198 Err(TraceMismatch {
199 expected: expected.to_vec(),
200 actual: actual.to_vec(),
201 })
202 }
203}