use crate::{
AuditEntry, ClearReason, CollectionDiffTrace, InvariantResultTrace, OutputFrameKind, OutputKey,
RebaselineReason, ResourceCommand, ResourceKey, ScopeId, ScopeLifecycleTrace,
StagedInputChange, TransactionId, TransactionPhase, TransactionResult,
};
use crate::{NodeId, Revision};
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TransactionTrace {
pub transaction_id: TransactionId,
pub revision: Revision,
pub staged_input_changes: Vec<StagedInputChange>,
pub changed_inputs: Vec<NodeId>,
pub dirty_roots: Vec<NodeId>,
pub recomputed_derived_nodes: Vec<NodeId>,
pub changed_derived_nodes: Vec<NodeId>,
pub recomputed_collection_nodes: Vec<NodeId>,
pub changed_collection_nodes: Vec<NodeId>,
pub collection_diffs: Vec<CollectionDiffTrace>,
pub resource_commands: Vec<ResourceCommandTrace>,
pub output_frames: Vec<OutputFrameTrace>,
pub scope_events: Vec<ScopeLifecycleTrace>,
pub audit_log: Vec<AuditEntry>,
pub phase_trace: Vec<TransactionPhase>,
pub invariant_results: Vec<InvariantResultTrace>,
}
impl TransactionTrace {
pub fn from_result<C, O>(result: &TransactionResult<C, O>) -> Self {
Self {
transaction_id: result.transaction_id,
revision: result.revision,
staged_input_changes: result.staged_input_changes.clone(),
changed_inputs: result.changed_inputs.clone(),
dirty_roots: result.dirty_roots.clone(),
recomputed_derived_nodes: result.recomputed_derived_nodes.clone(),
changed_derived_nodes: result.changed_derived_nodes.clone(),
recomputed_collection_nodes: result.recomputed_collection_nodes.clone(),
changed_collection_nodes: result.changed_collection_nodes.clone(),
collection_diffs: result.collection_diffs.clone(),
resource_commands: result
.resource_plan
.commands()
.iter()
.map(ResourceCommandTrace::from_command)
.collect(),
output_frames: result
.output_frames
.iter()
.map(|frame| OutputFrameTrace {
output_key: frame.output_key,
scope: frame.scope,
transaction_id: frame.transaction_id,
revision: frame.revision,
kind: OutputFrameKindTrace::from_kind(&frame.kind),
})
.collect(),
scope_events: result.scope_events.clone(),
audit_log: result.audit_log.clone(),
phase_trace: result.phase_trace.clone(),
invariant_results: result.invariant_results.clone(),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ResourceCommandTrace {
pub key: ResourceKey,
pub scope: ScopeId,
pub kind: ResourceCommandKind,
pub transition: ResourceTransitionPolicy,
}
impl ResourceCommandTrace {
fn from_command<C>(command: &ResourceCommand<C>) -> Self {
Self {
key: command.key().clone(),
scope: command.scope(),
kind: ResourceCommandKind::from_command(command),
transition: ResourceTransitionPolicy::from_command(command),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ResourceCommandKind {
Open,
Close,
Replace,
Refresh,
}
impl ResourceCommandKind {
pub(crate) fn from_command<C>(command: &ResourceCommand<C>) -> Self {
match command {
ResourceCommand::Open { .. } => Self::Open,
ResourceCommand::Close { .. } => Self::Close,
ResourceCommand::Replace { .. } => Self::Replace,
ResourceCommand::Refresh { .. } => Self::Refresh,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ResourceTransitionPolicy {
Open,
Close,
ReplaceAtomically,
Refresh,
Noop,
}
impl ResourceTransitionPolicy {
pub(crate) fn from_command<C>(command: &ResourceCommand<C>) -> Self {
match command {
ResourceCommand::Open { .. } => Self::Open,
ResourceCommand::Close { .. } => Self::Close,
ResourceCommand::Replace { .. } => Self::ReplaceAtomically,
ResourceCommand::Refresh { .. } => Self::Refresh,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OutputFrameTrace {
pub output_key: OutputKey,
pub scope: ScopeId,
pub transaction_id: TransactionId,
pub revision: Revision,
pub kind: OutputFrameKindTrace,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum OutputFrameKindTrace {
Baseline,
Delta,
Clear(ClearReason),
Rebaseline(RebaselineReason),
}
impl OutputFrameKindTrace {
pub(crate) fn from_kind<O>(kind: &OutputFrameKind<O>) -> Self {
match kind {
OutputFrameKind::Baseline(_) => Self::Baseline,
OutputFrameKind::Delta(_) => Self::Delta,
OutputFrameKind::Clear(reason) => Self::Clear(*reason),
OutputFrameKind::Rebaseline(_, reason) => Self::Rebaseline(*reason),
}
}
}
impl<C, O> TransactionResult<C, O> {
pub fn trace(&self) -> TransactionTrace {
TransactionTrace::from_result(self)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TraceMismatch {
pub expected: Vec<TransactionTrace>,
pub actual: Vec<TransactionTrace>,
}
pub fn assert_transaction_traces_match(
expected: &[TransactionTrace],
actual: &[TransactionTrace],
) -> Result<(), TraceMismatch> {
if expected == actual {
Ok(())
} else {
Err(TraceMismatch {
expected: expected.to_vec(),
actual: actual.to_vec(),
})
}
}