sqlx_ledger/
primitives.rs

1use 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}