sqlx_ledger/
primitives.rs1use crate::error::*;
2use rusty_money::{crypto, iso};
3
4use cel_interpreter::{CelResult, CelValue};
5use serde::{Deserialize, Serialize};
6
7crate::entity_id! { AccountId }
8crate::entity_id! { JournalId }
9crate::entity_id! { TransactionId }
10crate::entity_id! { EntryId }
11crate::entity_id! { TxTemplateId }
12crate::entity_id! { CorrelationId }
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type)]
15#[sqlx(type_name = "Layer", rename_all = "snake_case")]
16pub enum Layer {
17 Settled,
18 Pending,
19 Encumbered,
20}
21
22impl<'a> TryFrom<CelResult<'a>> for Layer {
23 type Error = SqlxLedgerError;
24
25 fn try_from(CelResult { val, .. }: CelResult) -> Result<Self, Self::Error> {
26 match val {
27 CelValue::String(v) if v.as_ref() == "SETTLED" => Ok(Layer::Settled),
28 CelValue::String(v) if v.as_ref() == "PENDING" => Ok(Layer::Pending),
29 CelValue::String(v) if v.as_ref() == "ENCUMBERED" => Ok(Layer::Encumbered),
30 v => Err(SqlxLedgerError::UnknownLayer(format!("{v:?}"))),
31 }
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type)]
36#[sqlx(type_name = "DebitOrCredit", rename_all = "snake_case")]
37pub enum DebitOrCredit {
38 Debit,
39 Credit,
40}
41
42impl<'a> TryFrom<CelResult<'a>> for DebitOrCredit {
43 type Error = SqlxLedgerError;
44
45 fn try_from(CelResult { val, .. }: CelResult) -> Result<Self, Self::Error> {
46 match val {
47 CelValue::String(v) if v.as_ref() == "DEBIT" => Ok(DebitOrCredit::Debit),
48 CelValue::String(v) if v.as_ref() == "CREDIT" => Ok(DebitOrCredit::Credit),
49 v => Err(SqlxLedgerError::UnknownDebitOrCredit(format!("{v:?}"))),
50 }
51 }
52}
53
54impl Default for DebitOrCredit {
55 fn default() -> Self {
56 Self::Credit
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type)]
61#[sqlx(type_name = "Status", rename_all = "snake_case")]
62pub enum Status {
63 Active,
64}
65
66impl Default for Status {
67 fn default() -> Self {
68 Self::Active
69 }
70}
71
72#[derive(Debug, Clone, Copy, Eq, Serialize, Deserialize)]
73#[serde(try_from = "String")]
74#[serde(into = "&str")]
75pub enum Currency {
76 Iso(&'static iso::Currency),
77 Crypto(&'static crypto::Currency),
78}
79
80impl Currency {
81 pub fn code(&self) -> &'static str {
82 match self {
83 Currency::Iso(c) => c.iso_alpha_code,
84 Currency::Crypto(c) => c.code,
85 }
86 }
87}
88
89impl std::fmt::Display for Currency {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 write!(f, "{}", self.code())
92 }
93}
94
95impl std::hash::Hash for Currency {
96 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
97 self.code().hash(state);
98 }
99}
100
101impl PartialEq for Currency {
102 fn eq(&self, other: &Self) -> bool {
103 self.code() == other.code()
104 }
105}
106
107impl std::str::FromStr for Currency {
108 type Err = SqlxLedgerError;
109
110 fn from_str(s: &str) -> Result<Self, Self::Err> {
111 match iso::find(s) {
112 Some(c) => Ok(Currency::Iso(c)),
113 _ => match crypto::find(s) {
114 Some(c) => Ok(Currency::Crypto(c)),
115 _ => Err(SqlxLedgerError::UnknownCurrency(s.to_string())),
116 },
117 }
118 }
119}
120
121impl TryFrom<String> for Currency {
122 type Error = SqlxLedgerError;
123
124 fn try_from(s: String) -> Result<Self, Self::Error> {
125 s.parse()
126 }
127}
128
129impl From<Currency> for &'static str {
130 fn from(c: Currency) -> Self {
131 c.code()
132 }
133}
134
135impl<'a> TryFrom<CelResult<'a>> for Currency {
136 type Error = SqlxLedgerError;
137
138 fn try_from(CelResult { val, .. }: CelResult) -> Result<Self, Self::Error> {
139 match val {
140 CelValue::String(v) => v.as_ref().parse(),
141 v => Err(SqlxLedgerError::UnknownCurrency(format!("{v:?}"))),
142 }
143 }
144}
145
146impl<'r, DB: sqlx::Database> sqlx::Decode<'r, DB> for Currency
147where
148 &'r str: sqlx::Decode<'r, DB>,
149{
150 fn decode(
151 value: <DB as sqlx::Database>::ValueRef<'r>,
152 ) -> Result<Currency, Box<dyn std::error::Error + 'static + Send + Sync>> {
153 let value = <&str as sqlx::Decode<DB>>::decode(value)?;
154
155 Ok(value.parse().map_err(Box::new)?)
156 }
157}