use crate::model::transfer::AddressType;
use pretty_simple_display::{DebugPretty, DisplaySimple};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum WithdrawalState {
#[default]
Unconfirmed,
Confirmed,
Cancelled,
Completed,
Interrupted,
Rejected,
}
impl WithdrawalState {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Unconfirmed => "unconfirmed",
Self::Confirmed => "confirmed",
Self::Cancelled => "cancelled",
Self::Completed => "completed",
Self::Interrupted => "interrupted",
Self::Rejected => "rejected",
}
}
#[must_use]
pub fn is_terminal(&self) -> bool {
matches!(
self,
Self::Cancelled | Self::Completed | Self::Interrupted | Self::Rejected
)
}
#[must_use]
pub fn is_successful(&self) -> bool {
matches!(self, Self::Completed)
}
}
impl std::fmt::Display for WithdrawalState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum WithdrawalPriority {
Insane,
ExtremeHigh,
VeryHigh,
#[default]
High,
Mid,
Low,
VeryLow,
}
impl WithdrawalPriority {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Insane => "insane",
Self::ExtremeHigh => "extreme_high",
Self::VeryHigh => "very_high",
Self::High => "high",
Self::Mid => "mid",
Self::Low => "low",
Self::VeryLow => "very_low",
}
}
}
impl std::fmt::Display for WithdrawalPriority {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DepositAddress {
pub address: String,
pub currency: String,
#[serde(rename = "type")]
pub address_type: AddressType,
pub creation_timestamp: i64,
}
impl DepositAddress {
#[must_use]
pub fn new(address: String, currency: String, creation_timestamp: i64) -> Self {
Self {
address,
currency,
address_type: AddressType::Deposit,
creation_timestamp,
}
}
}
#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Serialize, Deserialize)]
pub struct WithdrawalRequest {
pub currency: String,
pub address: String,
pub amount: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub priority: Option<WithdrawalPriority>,
}
impl WithdrawalRequest {
#[must_use]
pub fn new(currency: String, address: String, amount: f64) -> Self {
Self {
currency,
address,
amount,
priority: None,
}
}
#[must_use]
pub fn with_priority(mut self, priority: WithdrawalPriority) -> Self {
self.priority = Some(priority);
self
}
}
#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DepositId {
pub currency: String,
pub user_id: i64,
pub address: String,
pub tx_hash: String,
}
impl DepositId {
#[must_use]
pub fn new(currency: String, user_id: i64, address: String, tx_hash: String) -> Self {
Self {
currency,
user_id,
address,
tx_hash,
}
}
}
#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ClearanceOriginator {
pub is_personal: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub first_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub company_name: Option<String>,
pub address: String,
}
impl ClearanceOriginator {
#[must_use]
pub fn personal(address: String) -> Self {
Self {
is_personal: true,
first_name: None,
last_name: None,
company_name: None,
address,
}
}
#[must_use]
pub fn individual(first_name: String, last_name: String, address: String) -> Self {
Self {
is_personal: false,
first_name: Some(first_name),
last_name: Some(last_name),
company_name: None,
address,
}
}
#[must_use]
pub fn company(company_name: String, address: String) -> Self {
Self {
is_personal: false,
first_name: None,
last_name: None,
company_name: Some(company_name),
address,
}
}
#[must_use]
pub fn is_self_transfer(&self) -> bool {
self.is_personal
}
#[must_use]
pub fn is_individual(&self) -> bool {
!self.is_personal && self.first_name.is_some() && self.last_name.is_some()
}
#[must_use]
pub fn is_company(&self) -> bool {
!self.is_personal && self.company_name.is_some()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum ClearanceState {
#[default]
NotRequired,
InProgress,
Completed,
Failed,
}
impl ClearanceState {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::NotRequired => "not_required",
Self::InProgress => "in_progress",
Self::Completed => "completed",
Self::Failed => "failed",
}
}
#[must_use]
pub fn is_cleared(&self) -> bool {
matches!(self, Self::NotRequired | Self::Completed)
}
}
impl std::fmt::Display for ClearanceState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_withdrawal_state_default() {
let state = WithdrawalState::default();
assert_eq!(state, WithdrawalState::Unconfirmed);
}
#[test]
fn test_withdrawal_state_as_str() {
assert_eq!(WithdrawalState::Unconfirmed.as_str(), "unconfirmed");
assert_eq!(WithdrawalState::Confirmed.as_str(), "confirmed");
assert_eq!(WithdrawalState::Cancelled.as_str(), "cancelled");
assert_eq!(WithdrawalState::Completed.as_str(), "completed");
assert_eq!(WithdrawalState::Interrupted.as_str(), "interrupted");
assert_eq!(WithdrawalState::Rejected.as_str(), "rejected");
}
#[test]
fn test_withdrawal_state_is_terminal() {
assert!(!WithdrawalState::Unconfirmed.is_terminal());
assert!(!WithdrawalState::Confirmed.is_terminal());
assert!(WithdrawalState::Cancelled.is_terminal());
assert!(WithdrawalState::Completed.is_terminal());
assert!(WithdrawalState::Interrupted.is_terminal());
assert!(WithdrawalState::Rejected.is_terminal());
}
#[test]
fn test_withdrawal_state_is_successful() {
assert!(!WithdrawalState::Unconfirmed.is_successful());
assert!(!WithdrawalState::Cancelled.is_successful());
assert!(WithdrawalState::Completed.is_successful());
}
#[test]
fn test_withdrawal_state_serialization() {
let state = WithdrawalState::Completed;
let json = serde_json::to_string(&state).unwrap();
assert_eq!(json, "\"completed\"");
let deserialized: WithdrawalState = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, WithdrawalState::Completed);
}
#[test]
fn test_withdrawal_priority_default() {
let priority = WithdrawalPriority::default();
assert_eq!(priority, WithdrawalPriority::High);
}
#[test]
fn test_withdrawal_priority_as_str() {
assert_eq!(WithdrawalPriority::Insane.as_str(), "insane");
assert_eq!(WithdrawalPriority::ExtremeHigh.as_str(), "extreme_high");
assert_eq!(WithdrawalPriority::VeryHigh.as_str(), "very_high");
assert_eq!(WithdrawalPriority::High.as_str(), "high");
assert_eq!(WithdrawalPriority::Mid.as_str(), "mid");
assert_eq!(WithdrawalPriority::Low.as_str(), "low");
assert_eq!(WithdrawalPriority::VeryLow.as_str(), "very_low");
}
#[test]
fn test_withdrawal_priority_serialization() {
let priority = WithdrawalPriority::VeryHigh;
let json = serde_json::to_string(&priority).unwrap();
assert_eq!(json, "\"very_high\"");
let deserialized: WithdrawalPriority = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, WithdrawalPriority::VeryHigh);
}
#[test]
fn test_deposit_address_new() {
let addr = DepositAddress::new(
"bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh".to_string(),
"BTC".to_string(),
1640995200000,
);
assert_eq!(addr.address, "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh");
assert_eq!(addr.currency, "BTC");
assert_eq!(addr.address_type, AddressType::Deposit);
assert_eq!(addr.creation_timestamp, 1640995200000);
}
#[test]
fn test_deposit_address_serialization() {
let addr = DepositAddress::new(
"0x742d35Cc6634C0532925a3b844Bc9e7595f".to_string(),
"ETH".to_string(),
1640995200000,
);
let json = serde_json::to_string(&addr).unwrap();
let deserialized: DepositAddress = serde_json::from_str(&json).unwrap();
assert_eq!(addr, deserialized);
}
#[test]
fn test_withdrawal_request_new() {
let request = WithdrawalRequest::new(
"BTC".to_string(),
"bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh".to_string(),
1.5,
);
assert_eq!(request.currency, "BTC");
assert_eq!(request.amount, 1.5);
assert!(request.priority.is_none());
}
#[test]
fn test_withdrawal_request_with_priority() {
let request = WithdrawalRequest::new("BTC".to_string(), "address".to_string(), 1.0)
.with_priority(WithdrawalPriority::VeryHigh);
assert_eq!(request.priority, Some(WithdrawalPriority::VeryHigh));
}
#[test]
fn test_deposit_id_new() {
let deposit_id = DepositId::new(
"BTC".to_string(),
123,
"2NBqqD5GRJ8wHy1PYyCXTe9ke5226FhavBz".to_string(),
"230669110fdaf0a0dbcdc079b6b8b43d5af29cc73683835b9bc6b3406c065fda".to_string(),
);
assert_eq!(deposit_id.currency, "BTC");
assert_eq!(deposit_id.user_id, 123);
}
#[test]
fn test_deposit_id_serialization() {
let deposit_id = DepositId::new(
"BTC".to_string(),
123,
"address".to_string(),
"tx_hash".to_string(),
);
let json = serde_json::to_string(&deposit_id).unwrap();
let deserialized: DepositId = serde_json::from_str(&json).unwrap();
assert_eq!(deposit_id, deserialized);
}
#[test]
fn test_clearance_originator_personal() {
let originator = ClearanceOriginator::personal("123 Main St".to_string());
assert!(originator.is_personal);
assert!(originator.is_self_transfer());
assert!(!originator.is_individual());
assert!(!originator.is_company());
}
#[test]
fn test_clearance_originator_individual() {
let originator = ClearanceOriginator::individual(
"John".to_string(),
"Doe".to_string(),
"123 Main St".to_string(),
);
assert!(!originator.is_personal);
assert!(!originator.is_self_transfer());
assert!(originator.is_individual());
assert!(!originator.is_company());
assert_eq!(originator.first_name, Some("John".to_string()));
assert_eq!(originator.last_name, Some("Doe".to_string()));
}
#[test]
fn test_clearance_originator_company() {
let originator =
ClearanceOriginator::company("Acme Corp".to_string(), "456 Business Ave".to_string());
assert!(!originator.is_personal);
assert!(!originator.is_self_transfer());
assert!(!originator.is_individual());
assert!(originator.is_company());
assert_eq!(originator.company_name, Some("Acme Corp".to_string()));
}
#[test]
fn test_clearance_originator_serialization() {
let originator = ClearanceOriginator::individual(
"John".to_string(),
"Doe".to_string(),
"123 Main St".to_string(),
);
let json = serde_json::to_string(&originator).unwrap();
let deserialized: ClearanceOriginator = serde_json::from_str(&json).unwrap();
assert_eq!(originator, deserialized);
}
#[test]
fn test_clearance_state_default() {
let state = ClearanceState::default();
assert_eq!(state, ClearanceState::NotRequired);
}
#[test]
fn test_clearance_state_as_str() {
assert_eq!(ClearanceState::NotRequired.as_str(), "not_required");
assert_eq!(ClearanceState::InProgress.as_str(), "in_progress");
assert_eq!(ClearanceState::Completed.as_str(), "completed");
assert_eq!(ClearanceState::Failed.as_str(), "failed");
}
#[test]
fn test_clearance_state_is_cleared() {
assert!(ClearanceState::NotRequired.is_cleared());
assert!(!ClearanceState::InProgress.is_cleared());
assert!(ClearanceState::Completed.is_cleared());
assert!(!ClearanceState::Failed.is_cleared());
}
#[test]
fn test_clearance_state_serialization() {
let state = ClearanceState::InProgress;
let json = serde_json::to_string(&state).unwrap();
assert_eq!(json, "\"in_progress\"");
let deserialized: ClearanceState = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, ClearanceState::InProgress);
}
}