1use crate::{
2 AuditEntry, ClearReason, CollectionDiffTrace, InvariantResultTrace, OutputFrameKind, OutputKey,
3 RebaselineReason, ResourceCommand, ResourceKey, ScopeId, ScopeLifecycleTrace,
4 StagedInputChange, TransactionId, TransactionPhase, TransactionResult,
5};
6use crate::{NodeId, ResourceCoalescedTrace, 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 #[cfg_attr(
36 feature = "serde",
37 serde(default, skip_serializing_if = "Vec::is_empty")
38 )]
39 pub resource_coalescences: Vec<ResourceCoalescedTrace>,
40 pub output_frames: Vec<OutputFrameTrace>,
42 pub scope_events: Vec<ScopeLifecycleTrace>,
44 pub audit_log: Vec<AuditEntry>,
46 pub phase_trace: Vec<TransactionPhase>,
48 pub invariant_results: Vec<InvariantResultTrace>,
50}
51
52impl TransactionTrace {
53 pub fn from_result<C>(result: &TransactionResult<C>) -> Self {
55 Self {
56 transaction_id: result.transaction_id,
57 revision: result.revision,
58 staged_input_changes: result.staged_input_changes.clone(),
59 changed_inputs: result.changed_inputs.clone(),
60 dirty_roots: result.dirty_roots.clone(),
61 recomputed_derived_nodes: result.recomputed_derived_nodes.clone(),
62 changed_derived_nodes: result.changed_derived_nodes.clone(),
63 recomputed_collection_nodes: result.recomputed_collection_nodes.clone(),
64 changed_collection_nodes: result.changed_collection_nodes.clone(),
65 collection_diffs: result.collection_diffs.clone(),
66 resource_commands: result
67 .resource_plan
68 .commands()
69 .iter()
70 .map(ResourceCommandTrace::from_command)
71 .collect(),
72 resource_coalescences: result.resource_coalescences.clone(),
73 output_frames: result
74 .output_frames
75 .iter()
76 .map(|frame| OutputFrameTrace {
77 output_key: frame.output_key,
78 scope: frame.scope,
79 transaction_id: frame.transaction_id,
80 revision: frame.revision,
81 kind: OutputFrameKindTrace::from_kind(&frame.kind),
82 })
83 .collect(),
84 scope_events: result.scope_events.clone(),
85 audit_log: result.audit_log.clone(),
86 phase_trace: result.phase_trace.clone(),
87 invariant_results: result.invariant_results.clone(),
88 }
89 }
90}
91
92#[derive(Clone, Debug, Eq, PartialEq)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95pub struct ResourceCommandTrace {
96 pub key: ResourceKey,
98 pub scope: ScopeId,
100 pub kind: ResourceCommandKind,
102}
103
104impl ResourceCommandTrace {
105 fn from_command<C>(command: &ResourceCommand<C>) -> Self {
106 Self {
107 key: command.key().clone(),
108 scope: command.scope(),
109 kind: ResourceCommandKind::from_command(command),
110 }
111 }
112}
113
114#[derive(Copy, Clone, Debug, Eq, PartialEq)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub enum ResourceCommandKind {
118 Open,
120 Close,
122 Replace,
124 Refresh,
126}
127
128impl ResourceCommandKind {
129 pub(crate) fn from_command<C>(command: &ResourceCommand<C>) -> Self {
130 match command {
131 ResourceCommand::Open { .. } => Self::Open,
132 ResourceCommand::Close { .. } => Self::Close,
133 ResourceCommand::Replace { .. } => Self::Replace,
134 ResourceCommand::Refresh { .. } => Self::Refresh,
135 }
136 }
137}
138
139#[derive(Clone, Debug, Eq, PartialEq)]
141#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
142pub struct OutputFrameTrace {
143 pub output_key: OutputKey,
145 pub scope: ScopeId,
147 pub transaction_id: TransactionId,
149 pub revision: Revision,
151 pub kind: OutputFrameKindTrace,
153}
154
155#[derive(Copy, Clone, Debug, Eq, PartialEq)]
157#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
158pub enum OutputFrameKindTrace {
159 Baseline,
161 Delta,
163 Clear(ClearReason),
165 Rebaseline(RebaselineReason),
167}
168
169impl OutputFrameKindTrace {
170 pub(crate) fn from_kind(kind: &OutputFrameKind) -> Self {
171 match kind {
172 OutputFrameKind::Baseline(_) => Self::Baseline,
173 OutputFrameKind::Delta(_) => Self::Delta,
174 OutputFrameKind::Clear(reason) => Self::Clear(*reason),
175 OutputFrameKind::Rebaseline(_, reason) => Self::Rebaseline(*reason),
176 }
177 }
178}
179
180impl<C> TransactionResult<C> {
181 pub fn trace(&self) -> TransactionTrace {
183 TransactionTrace::from_result(self)
184 }
185}
186
187#[derive(Clone, Debug, Eq, PartialEq)]
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190pub struct TraceMismatch {
191 pub expected: Vec<TransactionTrace>,
193 pub actual: Vec<TransactionTrace>,
195}
196
197pub fn assert_transaction_traces_match(
199 expected: &[TransactionTrace],
200 actual: &[TransactionTrace],
201) -> Result<(), TraceMismatch> {
202 if expected == actual {
203 Ok(())
204 } else {
205 Err(TraceMismatch {
206 expected: expected.to_vec(),
207 actual: actual.to_vec(),
208 })
209 }
210}