postproject_core/
transaction.rs1use crate::{Error, ErrorKind, Result, TransactionId};
4
5#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
7pub enum TransactionState {
8 Open,
10 Committed,
12 RolledBack,
14}
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18pub struct TransactionLifecycle {
19 id: TransactionId,
20 state: TransactionState,
21}
22
23impl TransactionLifecycle {
24 #[must_use]
26 pub fn new() -> Self {
27 Self {
28 id: TransactionId::new(),
29 state: TransactionState::Open,
30 }
31 }
32
33 #[must_use]
35 pub const fn with_id(id: TransactionId) -> Self {
36 Self {
37 id,
38 state: TransactionState::Open,
39 }
40 }
41
42 #[must_use]
44 pub const fn id(self) -> TransactionId {
45 self.id
46 }
47
48 #[must_use]
50 pub const fn state(self) -> TransactionState {
51 self.state
52 }
53
54 pub fn ensure_open(self) -> Result<()> {
60 if self.state == TransactionState::Open {
61 Ok(())
62 } else {
63 Err(self.closed_error())
64 }
65 }
66
67 pub fn mark_committed(&mut self) -> Result<()> {
73 self.ensure_open()?;
74 self.state = TransactionState::Committed;
75 Ok(())
76 }
77
78 pub fn mark_rolled_back(&mut self) -> Result<()> {
84 self.ensure_open()?;
85 self.state = TransactionState::RolledBack;
86 Ok(())
87 }
88
89 fn closed_error(self) -> Error {
90 Error::new(
91 ErrorKind::Conflict,
92 format!("transaction {} is already {:?}", self.id, self.state),
93 )
94 }
95}
96
97impl Default for TransactionLifecycle {
98 fn default() -> Self {
99 Self::new()
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn commit_closes_transaction() {
109 let mut lifecycle = TransactionLifecycle::new();
110 lifecycle
111 .mark_committed()
112 .expect("open transaction commits");
113
114 assert_eq!(lifecycle.state(), TransactionState::Committed);
115 assert_eq!(
116 lifecycle
117 .mark_rolled_back()
118 .expect_err("committed transaction stays closed")
119 .kind(),
120 ErrorKind::Conflict
121 );
122 }
123
124 #[test]
125 fn rollback_closes_transaction() {
126 let mut lifecycle = TransactionLifecycle::new();
127 lifecycle
128 .mark_rolled_back()
129 .expect("open transaction rolls back");
130
131 assert_eq!(lifecycle.state(), TransactionState::RolledBack);
132 assert_eq!(
133 lifecycle
134 .mark_committed()
135 .expect_err("rolled-back transaction stays closed")
136 .kind(),
137 ErrorKind::Conflict
138 );
139 }
140}