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