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, Default, sqlx::Type)]
36#[sqlx(type_name = "DebitOrCredit", rename_all = "snake_case")]
37pub enum DebitOrCredit {
38 Debit,
39 #[default]
40 Credit,
41}
42
43impl<'a> TryFrom<CelResult<'a>> for DebitOrCredit {
44 type Error = SqlxLedgerError;
45
46 fn try_from(CelResult { val, .. }: CelResult) -> Result<Self, Self::Error> {
47 match val {
48 CelValue::String(v) if v.as_ref() == "DEBIT" => Ok(DebitOrCredit::Debit),
49 CelValue::String(v) if v.as_ref() == "CREDIT" => Ok(DebitOrCredit::Credit),
50 v => Err(SqlxLedgerError::UnknownDebitOrCredit(format!("{v:?}"))),
51 }
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, sqlx::Type)]
56#[sqlx(type_name = "Status", rename_all = "snake_case")]
57pub enum Status {
58 #[default]
59 Active,
60}
61
62#[derive(Debug, Clone, Copy, Eq, Serialize, Deserialize)]
63#[serde(try_from = "String")]
64#[serde(into = "&str")]
65pub enum Currency {
66 Iso(&'static iso::Currency),
67 Crypto(&'static crypto::Currency),
68}
69
70impl Currency {
71 pub fn code(&self) -> &'static str {
72 match self {
73 Currency::Iso(c) => c.iso_alpha_code,
74 Currency::Crypto(c) => c.code,
75 }
76 }
77}
78
79impl std::fmt::Display for Currency {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 write!(f, "{}", self.code())
82 }
83}
84
85impl std::hash::Hash for Currency {
86 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
87 self.code().hash(state);
88 }
89}
90
91impl PartialEq for Currency {
92 fn eq(&self, other: &Self) -> bool {
93 self.code() == other.code()
94 }
95}
96
97impl std::str::FromStr for Currency {
98 type Err = SqlxLedgerError;
99
100 fn from_str(s: &str) -> Result<Self, Self::Err> {
101 match iso::find(s) {
102 Some(c) => Ok(Currency::Iso(c)),
103 _ => match crypto::find(s) {
104 Some(c) => Ok(Currency::Crypto(c)),
105 _ => Err(SqlxLedgerError::UnknownCurrency(s.to_string())),
106 },
107 }
108 }
109}
110
111impl TryFrom<String> for Currency {
112 type Error = SqlxLedgerError;
113
114 fn try_from(s: String) -> Result<Self, Self::Error> {
115 s.parse()
116 }
117}
118
119impl From<Currency> for &'static str {
120 fn from(c: Currency) -> Self {
121 c.code()
122 }
123}
124
125impl<'a> TryFrom<CelResult<'a>> for Currency {
126 type Error = SqlxLedgerError;
127
128 fn try_from(CelResult { val, .. }: CelResult) -> Result<Self, Self::Error> {
129 match val {
130 CelValue::String(v) => v.as_ref().parse(),
131 v => Err(SqlxLedgerError::UnknownCurrency(format!("{v:?}"))),
132 }
133 }
134}
135
136impl<'r, DB: sqlx::Database> sqlx::Decode<'r, DB> for Currency
137where
138 &'r str: sqlx::Decode<'r, DB>,
139{
140 fn decode(
141 value: <DB as sqlx::Database>::ValueRef<'r>,
142 ) -> Result<Currency, Box<dyn std::error::Error + 'static + Send + Sync>> {
143 let value = <&str as sqlx::Decode<DB>>::decode(value)?;
144
145 Ok(value.parse().map_err(Box::new)?)
146 }
147}