Skip to main content

trellis_testing/
resource_state.rs

1use std::collections::BTreeSet;
2
3use trellis_core::{
4    ResourceCommand, ResourceCommandKind, ResourceTransitionPolicy, Revision, ScopeId,
5    TransactionId,
6};
7
8use crate::{HostStatusEvent, ResourceCommandContext};
9
10/// Applied resource command with transaction and generation context.
11#[derive(Clone, Debug, Eq, PartialEq)]
12pub struct ResourceCommandRecord<C = ()> {
13    /// Context common to all resource command assertions.
14    pub context: ResourceCommandContext,
15    /// Host-facing transition policy requested by the command.
16    pub transition: ResourceTransitionPolicy,
17    /// Retained host command payload for open/replace/refresh commands.
18    pub command: Option<C>,
19}
20
21impl<C: Clone> ResourceCommandRecord<C> {
22    pub(crate) fn from_command(
23        command: &ResourceCommand<C>,
24        transaction_id: TransactionId,
25        revision: Revision,
26        generation: u64,
27    ) -> Self {
28        Self {
29            context: ResourceCommandContext {
30                key: command.key().clone(),
31                scope: command.scope(),
32                transaction_id,
33                revision,
34                generation,
35                kind: resource_command_kind(command),
36            },
37            transition: resource_transition_policy(command),
38            command: retained_payload(command),
39        }
40    }
41}
42
43/// Current or historical ledger view for one resource key.
44#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct ResourceSnapshot<C = ()> {
46    /// Scopes that currently own the resource.
47    pub owners: BTreeSet<ScopeId>,
48    /// Whether the resource is currently open.
49    pub is_open: bool,
50    /// Number of open commands observed.
51    pub open_count: usize,
52    /// Number of close commands observed.
53    pub close_count: usize,
54    /// Number of replace commands observed.
55    pub replace_count: usize,
56    /// Latest command revision observed for this key.
57    pub command_revision: Revision,
58    /// Monotonic command generation assigned by the ledger.
59    pub generation: u64,
60    /// Last transaction that emitted a command for this key.
61    pub last_transaction_id: TransactionId,
62    /// Last accepted host status revision for this key.
63    pub last_status_revision: Option<Revision>,
64    /// Latest retained host command payload for this key.
65    pub current_command: Option<C>,
66    /// Latest injected host status accepted for this key.
67    pub injected_status: Option<HostStatusEvent>,
68    /// Last applied command record for this key.
69    pub last_command: ResourceCommandRecord<C>,
70}
71
72impl<C: Clone> ResourceSnapshot<C> {
73    pub(crate) fn new(record: ResourceCommandRecord<C>) -> Self {
74        Self {
75            owners: BTreeSet::new(),
76            is_open: true,
77            open_count: 0,
78            close_count: 0,
79            replace_count: 0,
80            command_revision: record.context.revision,
81            generation: record.context.generation,
82            last_transaction_id: record.context.transaction_id,
83            last_status_revision: None,
84            current_command: record.command.clone(),
85            injected_status: None,
86            last_command: record,
87        }
88    }
89
90    pub(crate) fn record_command(&mut self, record: ResourceCommandRecord<C>) {
91        self.command_revision = record.context.revision;
92        self.generation = record.context.generation;
93        self.last_transaction_id = record.context.transaction_id;
94        self.current_command = record.command.clone();
95        self.last_command = record;
96    }
97}
98
99impl<C> ResourceSnapshot<C> {
100    pub(crate) fn command_context(&self) -> ResourceCommandContext {
101        self.last_command.context.clone()
102    }
103}
104
105fn retained_payload<C: Clone>(command: &ResourceCommand<C>) -> Option<C> {
106    match command {
107        ResourceCommand::Open { command, .. }
108        | ResourceCommand::Replace { command, .. }
109        | ResourceCommand::Refresh { command, .. } => Some(command.clone()),
110        ResourceCommand::Close { .. } => None,
111    }
112}
113
114fn resource_command_kind<C>(command: &ResourceCommand<C>) -> ResourceCommandKind {
115    match command {
116        ResourceCommand::Open { .. } => ResourceCommandKind::Open,
117        ResourceCommand::Close { .. } => ResourceCommandKind::Close,
118        ResourceCommand::Replace { .. } => ResourceCommandKind::Replace,
119        ResourceCommand::Refresh { .. } => ResourceCommandKind::Refresh,
120    }
121}
122
123fn resource_transition_policy<C>(command: &ResourceCommand<C>) -> ResourceTransitionPolicy {
124    match command {
125        ResourceCommand::Open { .. } => ResourceTransitionPolicy::Open,
126        ResourceCommand::Close { .. } => ResourceTransitionPolicy::Close,
127        ResourceCommand::Replace { .. } => ResourceTransitionPolicy::ReplaceAtomically,
128        ResourceCommand::Refresh { .. } => ResourceTransitionPolicy::Refresh,
129    }
130}