123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- use crate::MaxLengthString;
- use serde::{Deserialize, Serialize};
- use std::collections::HashMap;
- /// Status type
- ///
- /// The statuses are bare strings of at most 10 characters
- pub type Status = MaxLengthString<10>;
- #[derive(Debug, Serialize, Deserialize)]
- /// Status manager
- ///
- /// This struct manages the status of the payments and their life cycle.
- pub struct StatusManager {
- /// List of all statuses
- statuses: Vec<Status>,
- /// List of all spendable statuses
- spendable: Vec<Status>,
- /// The default spendable status
- default_spendable: Status,
- /// List of all reverted statuses
- reverted: Vec<Status>,
- transition: HashMap<Status, Vec<Status>>,
- }
- #[derive(Debug, Clone, Copy)]
- /// Internal status type
- ///
- /// There could be many statuses, but they are translated into three types:
- pub enum InternalStatus {
- /// The payment is spendable, because it is final and it has been successfully processed
- Spendable,
- /// The payment is reverted, because it has been cancelled or failed
- Reverted,
- /// The payment is not created but still not final, it may be visible or not on the account's
- /// balance, but it is not spendable
- Pending,
- }
- /// Status error object
- #[derive(Debug, Serialize, thiserror::Error)]
- pub enum Error {
- #[error("Unknown status: {0}")]
- UnknownStatus(String),
- #[error("Invalid transition from {0} to {1}")]
- InvalidTransition(Status, Status),
- }
- impl StatusManager {
- /// Creates a new status from a given string
- pub fn new_status(&self, status_name: &str) -> Result<Status, Error> {
- self.statuses
- .iter()
- .find(|status| *status == status_name)
- .cloned()
- .ok_or(Error::UnknownStatus(status_name.to_owned()))
- }
- /// Translated the status into an internal status type
- pub fn internal_type(&self, status: &Status) -> InternalStatus {
- if self.is_spendable(status) {
- InternalStatus::Spendable
- } else if self.is_reverted(status) {
- InternalStatus::Reverted
- } else {
- InternalStatus::Pending
- }
- }
- /// Whether the status is spendable or not
- pub fn spendables(&self) -> &[Status] {
- &self.spendable
- }
- /// Whether the status is reverted or not
- pub fn default_spendable(&self) -> Status {
- self.default_spendable.clone()
- }
- /// Spendable statuses are the final state of a transaction that has finished as successfully
- /// therefore it is spendable.
- pub fn is_spendable(&self, status: &Status) -> bool {
- self.spendable.contains(status)
- }
- /// Reverted transactions are the final state of a transaction that has failed or cancelled and
- /// they inputs payments are reverted and spendedable.
- pub fn is_reverted(&self, status: &Status) -> bool {
- self.reverted.contains(status)
- }
- /// The transaction is final and cannot longer be updated
- pub fn is_final(&self, status: &Status) -> bool {
- self.is_spendable(status) || self.is_reverted(status)
- }
- /// Checks if the status transition is allowed
- pub fn is_valid_transition(&self, from: &Status, to: &Status) -> Result<(), Error> {
- if self.transition.get(from).map_or(false, |v| v.contains(to)) {
- Ok(())
- } else {
- Err(Error::InvalidTransition(from.clone(), to.clone()))
- }
- }
- }
- impl Default for StatusManager {
- fn default() -> Self {
- let pending: Status = "pending".into();
- let processing: Status = "processing".into();
- let cancelled: Status = "cancelled".into();
- let settled: Status = "settled".into();
- let failed: Status = "failed".into();
- Self {
- statuses: vec![
- pending.clone(),
- processing.clone(),
- cancelled.clone(),
- settled.clone(),
- failed.clone(),
- ],
- default_spendable: settled.clone(),
- spendable: vec![settled.clone()],
- reverted: vec![cancelled.clone(), failed.clone()],
- transition: {
- let mut map = HashMap::new();
- map.insert(
- pending.clone(),
- vec![processing.clone(), settled.clone(), cancelled.clone()],
- );
- map.insert(processing.clone(), vec![settled, failed]);
- map
- },
- }
- }
- }
|