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, 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}