123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- use serde::{Deserialize, Serialize};
- use strum_macros::Display;
- /// Transaction status
- #[derive(Clone, Eq, PartialEq, Debug, Display, Serialize, Deserialize)]
- #[serde(rename_all = "snake_case")]
- pub enum Status {
- /// Pending status
- ///
- /// A transaction has been created. At this status the transaction can be
- /// Cancelled.
- /// From Processing it can be moved to Settled or Failed.
- Pending,
- /// Processing
- ///
- /// The transaction leaves the Pending status and it cannot longer be
- /// Cancelled by an external event.
- Processing,
- /// Cancelled.
- ///
- /// The transaction has been Cancelled, the funds that were burned are
- /// released and can be used again.
- Cancelled,
- /// Settled
- ///
- /// The transaction is settled, the inputs used to create the transaction
- /// are forever burned.
- Settled,
- /// Failed
- ///
- /// The transaction has failed, the inputs used to create the transaction
- /// are released and can be used again.
- Failed,
- }
- impl Default for Status {
- fn default() -> Self {
- Self::Pending
- }
- }
- #[derive(thiserror::Error, Debug)]
- pub enum Error {
- #[error("Invalid status: {0}")]
- InvalidStatus(u32),
- }
- impl TryFrom<u32> for Status {
- type Error = Error;
- fn try_from(value: u32) -> Result<Self, Self::Error> {
- match value {
- 0 => Ok(Self::Pending),
- 10 => Ok(Self::Processing),
- 20 => Ok(Self::Cancelled),
- 30 => Ok(Self::Settled),
- 40 => Ok(Self::Failed),
- _ => Err(Error::InvalidStatus(value)),
- }
- }
- }
- impl From<Status> for u32 {
- fn from(value: Status) -> Self {
- (&value).into()
- }
- }
- impl From<&Status> for u32 {
- fn from(value: &Status) -> Self {
- match value {
- Status::Pending => 0,
- Status::Processing => 10,
- Status::Cancelled => 20,
- Status::Settled => 30,
- Status::Failed => 40,
- }
- }
- }
- impl Status {
- /// Checks if the current status can transition to the new state.
- pub fn can_transition_to(&self, new_status: &Status) -> bool {
- if self == new_status {
- false
- } else {
- match self {
- Self::Pending => true,
- Self::Processing => {
- matches!(new_status, Self::Settled | Self::Failed)
- }
- _ => false,
- }
- }
- }
- /// Checks if the current status is a rollback operation.
- ///
- /// In a rollback operation any previously spent payments are released by the storage layer.
- pub fn is_rollback(&self) -> bool {
- matches!(self, Self::Cancelled | Self::Failed)
- }
- /// Checks if the transaction status is finalized
- pub fn is_finalized(&self) -> bool {
- matches!(self, Self::Cancelled | Self::Settled | Self::Failed)
- }
- }
- #[cfg(test)]
- mod test {
- use super::*;
- #[test]
- fn pending() {
- let status = Status::Pending;
- assert!(!status.can_transition_to(&Status::Pending));
- assert!(status.can_transition_to(&Status::Processing));
- assert!(status.can_transition_to(&Status::Cancelled));
- assert!(status.can_transition_to(&Status::Settled));
- assert!(status.can_transition_to(&Status::Failed));
- assert!(!status.is_finalized());
- assert!(!status.is_rollback());
- }
- #[test]
- fn processing() {
- let status = Status::Processing;
- assert!(!status.can_transition_to(&Status::Pending));
- assert!(!status.can_transition_to(&Status::Processing));
- assert!(!status.can_transition_to(&Status::Cancelled));
- assert!(status.can_transition_to(&Status::Settled));
- assert!(status.can_transition_to(&Status::Failed));
- assert!(!status.is_finalized());
- assert!(!status.is_rollback());
- }
- #[test]
- fn cancelled() {
- let status = Status::Cancelled;
- assert!(!status.can_transition_to(&Status::Pending));
- assert!(!status.can_transition_to(&Status::Processing));
- assert!(!status.can_transition_to(&Status::Cancelled));
- assert!(!status.can_transition_to(&Status::Settled));
- assert!(!status.can_transition_to(&Status::Failed));
- assert!(status.is_finalized());
- assert!(status.is_rollback());
- }
- #[test]
- fn settled() {
- let status = Status::Settled;
- assert!(!status.can_transition_to(&Status::Pending));
- assert!(!status.can_transition_to(&Status::Processing));
- assert!(!status.can_transition_to(&Status::Cancelled));
- assert!(!status.can_transition_to(&Status::Settled));
- assert!(!status.can_transition_to(&Status::Failed));
- assert!(status.is_finalized());
- assert!(!status.is_rollback());
- }
- #[test]
- fn failed() {
- let status = Status::Failed;
- assert!(!status.can_transition_to(&Status::Pending));
- assert!(!status.can_transition_to(&Status::Processing));
- assert!(!status.can_transition_to(&Status::Cancelled));
- assert!(!status.can_transition_to(&Status::Settled));
- assert!(!status.can_transition_to(&Status::Failed));
- assert!(status.is_finalized());
- assert!(status.is_rollback());
- }
- }
|