cala_ledger_outbox_client/
convert.rs

1use cala_types::{
2    account::*, account_set::*, balance::*, entry::*, journal::*, outbox::*, primitives::*,
3    transaction::*, tx_template::*,
4};
5use cel_interpreter::CelExpression;
6
7use crate::{client::proto, error::*};
8
9impl TryFrom<proto::CalaLedgerEvent> for obix::out::PersistentOutboxEvent<OutboxEventPayload> {
10    type Error = CalaLedgerOutboxClientError;
11
12    fn try_from(event: proto::CalaLedgerEvent) -> Result<Self, Self::Error> {
13        let payload = OutboxEventPayload::try_from(
14            event
15                .payload
16                .ok_or(CalaLedgerOutboxClientError::MissingField)?,
17        )?;
18        Ok(obix::out::PersistentOutboxEvent {
19            id: event.id.parse::<obix::out::OutboxEventId>()?,
20            sequence: obix::EventSequence::from(event.sequence),
21            payload: Some(payload),
22            tracing_context: None,
23            recorded_at: event
24                .recorded_at
25                .ok_or(CalaLedgerOutboxClientError::MissingField)?
26                .into(),
27        })
28    }
29}
30
31impl TryFrom<proto::cala_ledger_event::Payload> for OutboxEventPayload {
32    type Error = CalaLedgerOutboxClientError;
33
34    fn try_from(payload: proto::cala_ledger_event::Payload) -> Result<Self, Self::Error> {
35        use cala_types::outbox::OutboxEventPayload::*;
36        let res = match payload {
37            proto::cala_ledger_event::Payload::AccountCreated(proto::AccountCreated {
38                data_source_id,
39                account,
40            }) => AccountCreated {
41                source: data_source_id.parse()?,
42                account: AccountValues::try_from(
43                    account.ok_or(CalaLedgerOutboxClientError::MissingField)?,
44                )?,
45            },
46            proto::cala_ledger_event::Payload::AccountUpdated(proto::AccountUpdated {
47                data_source_id,
48                account,
49                fields,
50            }) => AccountUpdated {
51                source: data_source_id.parse()?,
52                account: AccountValues::try_from(
53                    account.ok_or(CalaLedgerOutboxClientError::MissingField)?,
54                )?,
55                fields,
56            },
57            proto::cala_ledger_event::Payload::AccountSetCreated(proto::AccountSetCreated {
58                data_source_id,
59                account_set,
60            }) => AccountSetCreated {
61                source: data_source_id.parse()?,
62                account_set: AccountSetValues::try_from(
63                    account_set.ok_or(CalaLedgerOutboxClientError::MissingField)?,
64                )?,
65            },
66            proto::cala_ledger_event::Payload::AccountSetUpdated(proto::AccountSetUpdated {
67                data_source_id,
68                account_set,
69                fields,
70            }) => AccountSetUpdated {
71                source: data_source_id.parse()?,
72                account_set: AccountSetValues::try_from(
73                    account_set.ok_or(CalaLedgerOutboxClientError::MissingField)?,
74                )?,
75                fields,
76            },
77            proto::cala_ledger_event::Payload::AccountSetMemberCreated(
78                proto::AccountSetMemberCreated {
79                    data_source_id,
80                    member,
81                },
82            ) => {
83                let member = member.ok_or(CalaLedgerOutboxClientError::MissingField)?;
84                AccountSetMemberCreated {
85                    source: data_source_id.parse()?,
86                    account_set_id: member.account_set_id.parse()?,
87                    member_id: match member
88                        .member
89                        .ok_or(CalaLedgerOutboxClientError::MissingField)?
90                    {
91                        proto::account_set_member::Member::MemberAccountId(account_id) => {
92                            cala_types::account_set::AccountSetMemberId::from(
93                                account_id.parse::<AccountId>()?,
94                            )
95                        }
96                        proto::account_set_member::Member::MemberAccountSetId(account_set_id) => {
97                            cala_types::account_set::AccountSetMemberId::from(
98                                account_set_id.parse::<AccountSetId>()?,
99                            )
100                        }
101                    },
102                }
103            }
104            proto::cala_ledger_event::Payload::AccountSetMemberRemoved(
105                proto::AccountSetMemberRemoved {
106                    data_source_id,
107                    member,
108                },
109            ) => {
110                let member = member.ok_or(CalaLedgerOutboxClientError::MissingField)?;
111                AccountSetMemberRemoved {
112                    source: data_source_id.parse()?,
113                    account_set_id: member.account_set_id.parse()?,
114                    member_id: match member
115                        .member
116                        .ok_or(CalaLedgerOutboxClientError::MissingField)?
117                    {
118                        proto::account_set_member::Member::MemberAccountId(account_id) => {
119                            cala_types::account_set::AccountSetMemberId::from(
120                                account_id.parse::<AccountId>()?,
121                            )
122                        }
123                        proto::account_set_member::Member::MemberAccountSetId(account_set_id) => {
124                            cala_types::account_set::AccountSetMemberId::from(
125                                account_set_id.parse::<AccountSetId>()?,
126                            )
127                        }
128                    },
129                }
130            }
131            proto::cala_ledger_event::Payload::JournalCreated(proto::JournalCreated {
132                data_source_id,
133                journal,
134            }) => JournalCreated {
135                source: data_source_id.parse()?,
136                journal: JournalValues::try_from(
137                    journal.ok_or(CalaLedgerOutboxClientError::MissingField)?,
138                )?,
139            },
140            proto::cala_ledger_event::Payload::JournalUpdated(proto::JournalUpdated {
141                data_source_id,
142                journal,
143                fields,
144            }) => JournalUpdated {
145                source: data_source_id.parse()?,
146                journal: JournalValues::try_from(
147                    journal.ok_or(CalaLedgerOutboxClientError::MissingField)?,
148                )?,
149                fields,
150            },
151            proto::cala_ledger_event::Payload::TxTemplateCreated(proto::TxTemplateCreated {
152                data_source_id,
153                tx_template,
154            }) => TxTemplateCreated {
155                source: data_source_id.parse()?,
156                tx_template: TxTemplateValues::try_from(
157                    tx_template.ok_or(CalaLedgerOutboxClientError::MissingField)?,
158                )?,
159            },
160            proto::cala_ledger_event::Payload::TransactionCreated(proto::TransactionCreated {
161                data_source_id,
162                transaction,
163            }) => TransactionCreated {
164                source: data_source_id.parse()?,
165                transaction: TransactionValues::try_from(
166                    transaction.ok_or(CalaLedgerOutboxClientError::MissingField)?,
167                )?,
168            },
169            proto::cala_ledger_event::Payload::TransactionUpdated(proto::TransactionUpdated {
170                data_source_id,
171                transaction,
172                fields,
173            }) => TransactionUpdated {
174                source: data_source_id.parse()?,
175                transaction: TransactionValues::try_from(
176                    transaction.ok_or(CalaLedgerOutboxClientError::MissingField)?,
177                )?,
178                fields,
179            },
180            proto::cala_ledger_event::Payload::EntryCreated(proto::EntryCreated {
181                data_source_id,
182                entry,
183            }) => EntryCreated {
184                source: data_source_id.parse()?,
185                entry: EntryValues::try_from(
186                    entry.ok_or(CalaLedgerOutboxClientError::MissingField)?,
187                )?,
188            },
189            proto::cala_ledger_event::Payload::BalanceCreated(proto::BalanceCreated {
190                data_source_id,
191                balance,
192            }) => BalanceCreated {
193                source: data_source_id.parse()?,
194                balance: BalanceSnapshot::try_from(
195                    balance.ok_or(CalaLedgerOutboxClientError::MissingField)?,
196                )?,
197            },
198            proto::cala_ledger_event::Payload::BalanceUpdated(proto::BalanceUpdated {
199                data_source_id,
200                balance,
201            }) => BalanceUpdated {
202                source: data_source_id.parse()?,
203                balance: BalanceSnapshot::try_from(
204                    balance.ok_or(CalaLedgerOutboxClientError::MissingField)?,
205                )?,
206            },
207
208            proto::cala_ledger_event::Payload::Empty(_) => Empty,
209        };
210        Ok(res)
211    }
212}
213
214impl TryFrom<proto::Account> for AccountValues {
215    type Error = CalaLedgerOutboxClientError;
216
217    fn try_from(account: proto::Account) -> Result<Self, Self::Error> {
218        let metadata = account.metadata.map(serde_json::to_value).transpose()?;
219        let normal_balance_type =
220            proto::DebitOrCredit::try_from(account.normal_balance_type).map(DebitOrCredit::from)?;
221        let status = proto::Status::try_from(account.status).map(Status::from)?;
222        let res = Self {
223            id: account.id.parse()?,
224            version: account.version,
225            code: account.code,
226            name: account.name,
227            external_id: account.external_id,
228            normal_balance_type,
229            status,
230            description: account.description,
231            metadata,
232            config: AccountConfig::from(
233                account
234                    .config
235                    .ok_or(CalaLedgerOutboxClientError::MissingField)?,
236            ),
237        };
238        Ok(res)
239    }
240}
241
242impl From<proto::AccountConfig> for AccountConfig {
243    fn from(config: proto::AccountConfig) -> Self {
244        Self {
245            is_account_set: config.is_account_set,
246            eventually_consistent: config.eventually_consistent,
247        }
248    }
249}
250
251impl TryFrom<proto::AccountSet> for AccountSetValues {
252    type Error = CalaLedgerOutboxClientError;
253
254    fn try_from(account_set: proto::AccountSet) -> Result<Self, Self::Error> {
255        let metadata = account_set.metadata.map(serde_json::to_value).transpose()?;
256        let normal_balance_type = proto::DebitOrCredit::try_from(account_set.normal_balance_type)
257            .map(DebitOrCredit::from)?;
258        let res = Self {
259            id: account_set.id.parse()?,
260            version: account_set.version,
261            journal_id: account_set.journal_id.parse()?,
262            name: account_set.name,
263            external_id: account_set.external_id,
264            normal_balance_type,
265            description: account_set.description,
266            metadata,
267        };
268        Ok(res)
269    }
270}
271
272impl TryFrom<proto::Journal> for JournalValues {
273    type Error = CalaLedgerOutboxClientError;
274
275    fn try_from(journal: proto::Journal) -> Result<Self, Self::Error> {
276        let status = proto::Status::try_from(journal.status).map(Status::from)?;
277        let res = Self {
278            id: journal.id.parse()?,
279            version: journal.version,
280            name: journal.name,
281            code: journal.code,
282            status,
283            description: journal.description,
284            config: JournalConfig::from(
285                journal
286                    .config
287                    .ok_or(CalaLedgerOutboxClientError::MissingField)?,
288            ),
289        };
290        Ok(res)
291    }
292}
293
294impl From<proto::JournalConfig> for JournalConfig {
295    fn from(config: proto::JournalConfig) -> Self {
296        Self {
297            enable_effective_balances: config.enable_effective_balances,
298        }
299    }
300}
301
302impl From<proto::DebitOrCredit> for DebitOrCredit {
303    fn from(dc: proto::DebitOrCredit) -> Self {
304        match dc {
305            proto::DebitOrCredit::Debit => DebitOrCredit::Debit,
306            proto::DebitOrCredit::Credit => DebitOrCredit::Credit,
307        }
308    }
309}
310
311impl From<proto::Status> for Status {
312    fn from(status: proto::Status) -> Self {
313        match status {
314            proto::Status::Active => Status::Active,
315            proto::Status::Locked => Status::Locked,
316        }
317    }
318}
319
320impl TryFrom<proto::TxTemplate> for TxTemplateValues {
321    type Error = CalaLedgerOutboxClientError;
322
323    fn try_from(
324        proto::TxTemplate {
325            id,
326            version,
327            code,
328            params,
329            transaction,
330            entries,
331            description,
332            metadata,
333        }: proto::TxTemplate,
334    ) -> Result<Self, Self::Error> {
335        let params = params
336            .into_iter()
337            .map(ParamDefinition::try_from)
338            .collect::<Result<Vec<_>, _>>()?;
339        let transaction = TxTemplateTransaction::try_from(
340            transaction.ok_or(CalaLedgerOutboxClientError::MissingField)?,
341        )?;
342        let entries = entries
343            .into_iter()
344            .map(TxTemplateEntry::try_from)
345            .collect::<Result<Vec<_>, _>>()?;
346
347        let res = Self {
348            id: id.parse()?,
349            version,
350            code,
351            params: Some(params),
352            transaction,
353            entries,
354            description,
355            metadata: metadata.map(serde_json::to_value).transpose()?,
356        };
357        Ok(res)
358    }
359}
360
361impl TryFrom<proto::ParamDefinition> for ParamDefinition {
362    type Error = CalaLedgerOutboxClientError;
363    fn try_from(
364        proto::ParamDefinition {
365            name,
366            data_type,
367            default,
368            description,
369        }: proto::ParamDefinition,
370    ) -> Result<Self, Self::Error> {
371        let res = Self {
372            name,
373            r#type: proto::ParamDataType::try_from(data_type).map(ParamDataType::from)?,
374            default: default.map(CelExpression::try_from).transpose()?,
375            description,
376        };
377        Ok(res)
378    }
379}
380
381impl TryFrom<proto::TxTemplateTransaction> for TxTemplateTransaction {
382    type Error = CalaLedgerOutboxClientError;
383    fn try_from(
384        proto::TxTemplateTransaction {
385            effective,
386            journal_id,
387            correlation_id,
388            external_id,
389            description,
390            metadata,
391        }: proto::TxTemplateTransaction,
392    ) -> Result<Self, Self::Error> {
393        let res = Self {
394            effective: CelExpression::try_from(effective)?,
395            journal_id: CelExpression::try_from(journal_id)?,
396            correlation_id: correlation_id.map(CelExpression::try_from).transpose()?,
397            external_id: external_id.map(CelExpression::try_from).transpose()?,
398            description: description.map(CelExpression::try_from).transpose()?,
399            metadata: metadata.map(CelExpression::try_from).transpose()?,
400        };
401        Ok(res)
402    }
403}
404
405impl TryFrom<proto::TxTemplateEntry> for TxTemplateEntry {
406    type Error = CalaLedgerOutboxClientError;
407    fn try_from(
408        proto::TxTemplateEntry {
409            entry_type,
410            account_id,
411            layer,
412            direction,
413            units,
414            currency,
415            description,
416            metadata,
417        }: proto::TxTemplateEntry,
418    ) -> Result<Self, Self::Error> {
419        let res = Self {
420            entry_type: CelExpression::try_from(entry_type)?,
421            account_id: CelExpression::try_from(account_id)?,
422            layer: CelExpression::try_from(layer)?,
423            direction: CelExpression::try_from(direction)?,
424            units: CelExpression::try_from(units)?,
425            currency: CelExpression::try_from(currency)?,
426            description: description.map(CelExpression::try_from).transpose()?,
427            metadata: metadata.map(CelExpression::try_from).transpose()?,
428        };
429        Ok(res)
430    }
431}
432
433impl From<proto::ParamDataType> for ParamDataType {
434    fn from(data_type: proto::ParamDataType) -> Self {
435        match data_type {
436            proto::ParamDataType::String => ParamDataType::String,
437            proto::ParamDataType::Integer => ParamDataType::Integer,
438            proto::ParamDataType::Decimal => ParamDataType::Decimal,
439            proto::ParamDataType::Boolean => ParamDataType::Boolean,
440            proto::ParamDataType::Uuid => ParamDataType::Uuid,
441            proto::ParamDataType::Date => ParamDataType::Date,
442            proto::ParamDataType::Timestamp => ParamDataType::Timestamp,
443            proto::ParamDataType::Json => ParamDataType::Json,
444        }
445    }
446}
447
448impl TryFrom<proto::Transaction> for TransactionValues {
449    type Error = CalaLedgerOutboxClientError;
450    fn try_from(
451        proto::Transaction {
452            id,
453            version,
454            created_at,
455            modified_at,
456            journal_id,
457            tx_template_id,
458            entry_ids,
459            effective,
460            correlation_id,
461            external_id,
462            description,
463            metadata,
464            void_of,
465            voided_by,
466        }: proto::Transaction,
467    ) -> Result<Self, Self::Error> {
468        let res = Self {
469            id: id.parse()?,
470            version,
471            created_at: created_at
472                .ok_or(CalaLedgerOutboxClientError::MissingField)?
473                .into(),
474            modified_at: modified_at
475                .ok_or(CalaLedgerOutboxClientError::MissingField)?
476                .into(),
477            journal_id: journal_id.parse()?,
478            tx_template_id: tx_template_id.parse()?,
479            entry_ids: entry_ids
480                .into_iter()
481                .map(|id| id.parse())
482                .collect::<Result<_, _>>()?,
483            effective: effective.parse()?,
484            voided_by: voided_by.map(|id| id.parse()).transpose()?,
485            void_of: void_of.map(|id| id.parse()).transpose()?,
486            correlation_id,
487            external_id,
488            description,
489            metadata: metadata.map(serde_json::to_value).transpose()?,
490        };
491        Ok(res)
492    }
493}
494
495impl TryFrom<proto::Entry> for EntryValues {
496    type Error = CalaLedgerOutboxClientError;
497    fn try_from(
498        proto::Entry {
499            id,
500            version,
501            journal_id,
502            transaction_id,
503            entry_type,
504            sequence,
505            account_id,
506            layer,
507            direction,
508            units,
509            currency,
510            description,
511            metadata,
512        }: proto::Entry,
513    ) -> Result<Self, Self::Error> {
514        let res = Self {
515            id: id.parse()?,
516            version,
517            journal_id: journal_id.parse()?,
518            transaction_id: transaction_id.parse()?,
519            account_id: account_id.parse()?,
520            entry_type,
521            sequence,
522            layer: proto::Layer::try_from(layer).map(Layer::from)?,
523            direction: proto::DebitOrCredit::try_from(direction).map(DebitOrCredit::from)?,
524            units: units.parse()?,
525            currency: currency.parse::<Currency>()?,
526            description,
527            metadata: metadata.map(serde_json::to_value).transpose()?,
528        };
529        Ok(res)
530    }
531}
532
533impl TryFrom<proto::Balance> for BalanceSnapshot {
534    type Error = CalaLedgerOutboxClientError;
535    fn try_from(
536        proto::Balance {
537            journal_id,
538            account_id,
539            currency,
540            version,
541            created_at,
542            modified_at,
543            entry_id,
544            settled,
545            pending,
546            encumbrance,
547        }: proto::Balance,
548    ) -> Result<Self, Self::Error> {
549        let res = Self {
550            journal_id: journal_id.parse()?,
551            account_id: account_id.parse()?,
552            currency: currency.parse()?,
553            version,
554            created_at: created_at
555                .ok_or(CalaLedgerOutboxClientError::MissingField)?
556                .into(),
557            modified_at: modified_at
558                .ok_or(CalaLedgerOutboxClientError::MissingField)?
559                .into(),
560            entry_id: entry_id.parse()?,
561            settled: BalanceAmount::try_from(
562                settled.ok_or(CalaLedgerOutboxClientError::MissingField)?,
563            )?,
564            pending: BalanceAmount::try_from(
565                pending.ok_or(CalaLedgerOutboxClientError::MissingField)?,
566            )?,
567            encumbrance: BalanceAmount::try_from(
568                encumbrance.ok_or(CalaLedgerOutboxClientError::MissingField)?,
569            )?,
570        };
571        Ok(res)
572    }
573}
574
575impl TryFrom<proto::BalanceAmount> for BalanceAmount {
576    type Error = CalaLedgerOutboxClientError;
577
578    fn try_from(amount: proto::BalanceAmount) -> Result<Self, Self::Error> {
579        let res = Self {
580            dr_balance: amount.dr_balance.parse()?,
581            cr_balance: amount.cr_balance.parse()?,
582            entry_id: amount.entry_id.parse()?,
583            modified_at: amount
584                .modified_at
585                .ok_or(CalaLedgerOutboxClientError::MissingField)?
586                .into(),
587        };
588        Ok(res)
589    }
590}
591
592impl From<proto::Layer> for Layer {
593    fn from(layer: proto::Layer) -> Self {
594        match layer {
595            proto::Layer::Settled => Layer::Settled,
596            proto::Layer::Pending => Layer::Pending,
597            proto::Layer::Encumbrance => Layer::Encumbrance,
598        }
599    }
600}