Skip to main content

syrup_rail_postgres/
transactions.rs

1use std::{error::Error, fmt, time::Duration};
2
3use async_trait::async_trait;
4use sqlx::PgConnection;
5use syrup_rail::{BillingEvent, BillingEventSubject};
6
7type BoxError = Box<dyn Error + Send + Sync + 'static>;
8
9#[derive(Debug)]
10pub struct BillingTransactionError {
11    source: BoxError,
12}
13
14impl BillingTransactionError {
15    pub fn new(source: impl Error + Send + Sync + 'static) -> Self {
16        Self {
17            source: Box::new(source),
18        }
19    }
20
21    pub fn into_source(self) -> BoxError {
22        self.source
23    }
24}
25
26impl fmt::Display for BillingTransactionError {
27    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
28        formatter.write_str("billing transaction operation failed")
29    }
30}
31
32impl Error for BillingTransactionError {
33    fn source(&self) -> Option<&(dyn Error + 'static)> {
34        Some(self.source.as_ref())
35    }
36}
37
38#[derive(Debug)]
39pub struct BillingEventWriteError {
40    source: BoxError,
41}
42
43impl BillingEventWriteError {
44    pub fn new(source: impl Error + Send + Sync + 'static) -> Self {
45        Self {
46            source: Box::new(source),
47        }
48    }
49
50    pub fn into_source(self) -> BoxError {
51        self.source
52    }
53}
54
55impl fmt::Display for BillingEventWriteError {
56    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
57        formatter.write_str("billing event append failed")
58    }
59}
60
61impl Error for BillingEventWriteError {
62    fn source(&self) -> Option<&(dyn Error + 'static)> {
63        Some(self.source.as_ref())
64    }
65}
66
67#[derive(Clone, Copy, Debug, Eq, PartialEq)]
68pub enum BillingTransactionSubjectState {
69    LiveRecipient,
70    RetainedSubject,
71}
72
73/// Host-prepared transaction whose subject authorization lock is acquired
74/// before any shared billing lock.
75#[async_trait]
76pub trait BillingTransactionCoordinator: Send + Sync {
77    async fn begin(
78        &self,
79        subject: BillingEventSubject,
80        lock_timeout: Duration,
81    ) -> Result<Box<dyn BillingTransaction>, BillingTransactionError>;
82}
83
84/// One host-owned transaction and its typed event projection capability.
85///
86/// The returned connection is the same transaction on which `append_event`
87/// and `commit` operate. Implementations must not acquire another connection.
88#[async_trait]
89pub trait BillingTransaction: Send {
90    fn connection(&mut self) -> &mut PgConnection;
91
92    fn subject_state(&self) -> BillingTransactionSubjectState;
93
94    async fn append_event(&mut self, event: &BillingEvent) -> Result<(), BillingEventWriteError>;
95
96    async fn commit(self: Box<Self>) -> Result<(), BillingTransactionError>;
97
98    async fn rollback(self: Box<Self>) -> Result<(), BillingTransactionError>;
99}