Skip to main content

rustlavel_ledger/
entry.rs

1//! A transfer, and the two entries it writes.
2
3use rustlavel_core::Json;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum TransferKind {
7    /// Credits in, from `system:topup`.
8    TopUp,
9    /// Credits spent, to `system:consumption`.
10    Consume,
11    /// A hold being captured. Same direction as `Consume`, kept distinct so
12    /// the history says which.
13    Capture,
14    /// Credits that ran out, to `system:expiry`.
15    Expire,
16    /// A correction by an operator. Either direction.
17    Adjust,
18    /// Credits given back, from `system:consumption`.
19    Refund,
20}
21
22impl TransferKind {
23    pub fn as_str(self) -> &'static str {
24        match self {
25            TransferKind::TopUp => "topup",
26            TransferKind::Consume => "consume",
27            TransferKind::Capture => "capture",
28            TransferKind::Expire => "expire",
29            TransferKind::Adjust => "adjust",
30            TransferKind::Refund => "refund",
31        }
32    }
33
34    pub fn parse(text: &str) -> Option<TransferKind> {
35        Some(match text {
36            "topup" => TransferKind::TopUp,
37            "consume" => TransferKind::Consume,
38            "capture" => TransferKind::Capture,
39            "expire" => TransferKind::Expire,
40            "adjust" => TransferKind::Adjust,
41            "refund" => TransferKind::Refund,
42            _ => return None,
43        })
44    }
45}
46
47/// One movement of value between two accounts.
48#[derive(Debug, Clone, PartialEq)]
49pub struct Transfer {
50    pub id: String,
51    /// The caller's idempotency key — a payment id, a job id. Unique: a second
52    /// transfer under the same reference returns the first rather than moving
53    /// the money again, which is what stops a retried webhook crediting twice.
54    pub reference: String,
55    pub kind: TransferKind,
56    pub from_account: String,
57    pub to_account: String,
58    /// Always positive. Direction is the two account fields.
59    pub amount: i64,
60    pub created_at: u64,
61    pub metadata: Json,
62}
63
64impl Transfer {
65    pub fn to_json(&self) -> Json {
66        Json::object([
67            ("id", Json::from(self.id.as_str())),
68            ("reference", Json::from(self.reference.as_str())),
69            ("kind", Json::from(self.kind.as_str())),
70            ("from", Json::from(self.from_account.as_str())),
71            ("to", Json::from(self.to_account.as_str())),
72            ("amount", Json::from(self.amount)),
73            ("created_at", Json::from(self.created_at as i64)),
74            ("metadata", self.metadata.clone()),
75        ])
76    }
77}
78
79/// One side of a transfer, on one account. Two per transfer, equal and
80/// opposite.
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct Entry {
83    pub id: i64,
84    pub transfer_id: String,
85    pub account_id: String,
86    /// Signed: negative is a debit, positive a credit.
87    pub amount: i64,
88    /// The account's balance once this entry was applied. A history that
89    /// carries its running balance can be checked line by line.
90    pub balance_after: i64,
91    pub kind: TransferKind,
92    pub reference: String,
93    pub created_at: u64,
94}
95
96impl Entry {
97    pub fn to_json(&self) -> Json {
98        Json::object([
99            ("id", Json::from(self.id)),
100            ("transfer_id", Json::from(self.transfer_id.as_str())),
101            ("amount", Json::from(self.amount)),
102            ("balance_after", Json::from(self.balance_after)),
103            ("kind", Json::from(self.kind.as_str())),
104            ("reference", Json::from(self.reference.as_str())),
105            ("created_at", Json::from(self.created_at as i64)),
106        ])
107    }
108}