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::EntryCreated(proto::EntryCreated {
169                data_source_id,
170                entry,
171            }) => EntryCreated {
172                source: data_source_id.parse()?,
173                entry: EntryValues::try_from(
174                    entry.ok_or(CalaLedgerOutboxClientError::MissingField)?,
175                )?,
176            },
177            proto::cala_ledger_event::Payload::BalanceCreated(proto::BalanceCreated {
178                data_source_id,
179                balance,
180            }) => BalanceCreated {
181                source: data_source_id.parse()?,
182                balance: BalanceSnapshot::try_from(
183                    balance.ok_or(CalaLedgerOutboxClientError::MissingField)?,
184                )?,
185            },
186            proto::cala_ledger_event::Payload::BalanceUpdated(proto::BalanceUpdated {
187                data_source_id,
188                balance,
189            }) => BalanceUpdated {
190                source: data_source_id.parse()?,
191                balance: BalanceSnapshot::try_from(
192                    balance.ok_or(CalaLedgerOutboxClientError::MissingField)?,
193                )?,
194            },
195
196            proto::cala_ledger_event::Payload::Empty(_) => Empty,
197        };
198        Ok(res)
199    }
200}
201
202impl TryFrom<proto::Account> for AccountValues {
203    type Error = CalaLedgerOutboxClientError;
204
205    fn try_from(account: proto::Account) -> Result<Self, Self::Error> {
206        let metadata = account.metadata.map(serde_json::to_value).transpose()?;
207        let normal_balance_type =
208            proto::DebitOrCredit::try_from(account.normal_balance_type).map(DebitOrCredit::from)?;
209        let status = proto::Status::try_from(account.status).map(Status::from)?;
210        let res = Self {
211            id: account.id.parse()?,
212            version: account.version,
213            code: account.code,
214            name: account.name,
215            external_id: account.external_id,
216            normal_balance_type,
217            status,
218            description: account.description,
219            metadata,
220            config: AccountConfig::from(
221                account
222                    .config
223                    .ok_or(CalaLedgerOutboxClientError::MissingField)?,
224            ),
225        };
226        Ok(res)
227    }
228}
229
230impl From<proto::AccountConfig> for AccountConfig {
231    fn from(config: proto::AccountConfig) -> Self {
232        Self {
233            is_account_set: config.is_account_set,
234            eventually_consistent: config.eventually_consistent,
235        }
236    }
237}
238
239impl TryFrom<proto::AccountSet> for AccountSetValues {
240    type Error = CalaLedgerOutboxClientError;
241
242    fn try_from(account_set: proto::AccountSet) -> Result<Self, Self::Error> {
243        let metadata = account_set.metadata.map(serde_json::to_value).transpose()?;
244        let normal_balance_type = proto::DebitOrCredit::try_from(account_set.normal_balance_type)
245            .map(DebitOrCredit::from)?;
246        let res = Self {
247            id: account_set.id.parse()?,
248            version: account_set.version,
249            journal_id: account_set.journal_id.parse()?,
250            name: account_set.name,
251            external_id: account_set.external_id,
252            normal_balance_type,
253            description: account_set.description,
254            metadata,
255        };
256        Ok(res)
257    }
258}
259
260impl TryFrom<proto::Journal> for JournalValues {
261    type Error = CalaLedgerOutboxClientError;
262
263    fn try_from(journal: proto::Journal) -> Result<Self, Self::Error> {
264        let status = proto::Status::try_from(journal.status).map(Status::from)?;
265        let res = Self {
266            id: journal.id.parse()?,
267            version: journal.version,
268            name: journal.name,
269            code: journal.code,
270            status,
271            description: journal.description,
272            config: JournalConfig::from(
273                journal
274                    .config
275                    .ok_or(CalaLedgerOutboxClientError::MissingField)?,
276            ),
277        };
278        Ok(res)
279    }
280}
281
282impl From<proto::JournalConfig> for JournalConfig {
283    fn from(config: proto::JournalConfig) -> Self {
284        Self {
285            enable_effective_balances: config.enable_effective_balances,
286        }
287    }
288}
289
290impl From<proto::DebitOrCredit> for DebitOrCredit {
291    fn from(dc: proto::DebitOrCredit) -> Self {
292        match dc {
293            proto::DebitOrCredit::Debit => DebitOrCredit::Debit,
294            proto::DebitOrCredit::Credit => DebitOrCredit::Credit,
295        }
296    }
297}
298
299impl From<proto::Status> for Status {
300    fn from(status: proto::Status) -> Self {
301        match status {
302            proto::Status::Active => Status::Active,
303            proto::Status::Locked => Status::Locked,
304        }
305    }
306}
307
308impl TryFrom<proto::TxTemplate> for TxTemplateValues {
309    type Error = CalaLedgerOutboxClientError;
310
311    fn try_from(
312        proto::TxTemplate {
313            id,
314            version,
315            code,
316            params,
317            transaction,
318            entries,
319            description,
320            metadata,
321        }: proto::TxTemplate,
322    ) -> Result<Self, Self::Error> {
323        let params = params
324            .into_iter()
325            .map(ParamDefinition::try_from)
326            .collect::<Result<Vec<_>, _>>()?;
327        let transaction = TxTemplateTransaction::try_from(
328            transaction.ok_or(CalaLedgerOutboxClientError::MissingField)?,
329        )?;
330        let entries = entries
331            .into_iter()
332            .map(TxTemplateEntry::try_from)
333            .collect::<Result<Vec<_>, _>>()?;
334
335        let res = Self {
336            id: id.parse()?,
337            version,
338            code,
339            params: Some(params),
340            transaction,
341            entries,
342            description,
343            metadata: metadata.map(serde_json::to_value).transpose()?,
344        };
345        Ok(res)
346    }
347}
348
349impl TryFrom<proto::ParamDefinition> for ParamDefinition {
350    type Error = CalaLedgerOutboxClientError;
351    fn try_from(
352        proto::ParamDefinition {
353            name,
354            data_type,
355            default,
356            description,
357        }: proto::ParamDefinition,
358    ) -> Result<Self, Self::Error> {
359        let res = Self {
360            name,
361            r#type: proto::ParamDataType::try_from(data_type).map(ParamDataType::from)?,
362            default: default.map(CelExpression::try_from).transpose()?,
363            description,
364        };
365        Ok(res)
366    }
367}
368
369impl TryFrom<proto::TxTemplateTransaction> for TxTemplateTransaction {
370    type Error = CalaLedgerOutboxClientError;
371    fn try_from(
372        proto::TxTemplateTransaction {
373            effective,
374            journal_id,
375            correlation_id,
376            external_id,
377            description,
378            metadata,
379        }: proto::TxTemplateTransaction,
380    ) -> Result<Self, Self::Error> {
381        let res = Self {
382            effective: CelExpression::try_from(effective)?,
383            journal_id: CelExpression::try_from(journal_id)?,
384            correlation_id: correlation_id.map(CelExpression::try_from).transpose()?,
385            external_id: external_id.map(CelExpression::try_from).transpose()?,
386            description: description.map(CelExpression::try_from).transpose()?,
387            metadata: metadata.map(CelExpression::try_from).transpose()?,
388        };
389        Ok(res)
390    }
391}
392
393impl TryFrom<proto::TxTemplateEntry> for TxTemplateEntry {
394    type Error = CalaLedgerOutboxClientError;
395    fn try_from(
396        proto::TxTemplateEntry {
397            entry_type,
398            account_id,
399            layer,
400            direction,
401            units,
402            currency,
403            description,
404            metadata,
405        }: proto::TxTemplateEntry,
406    ) -> Result<Self, Self::Error> {
407        let res = Self {
408            entry_type: CelExpression::try_from(entry_type)?,
409            account_id: CelExpression::try_from(account_id)?,
410            layer: CelExpression::try_from(layer)?,
411            direction: CelExpression::try_from(direction)?,
412            units: CelExpression::try_from(units)?,
413            currency: CelExpression::try_from(currency)?,
414            description: description.map(CelExpression::try_from).transpose()?,
415            metadata: metadata.map(CelExpression::try_from).transpose()?,
416        };
417        Ok(res)
418    }
419}
420
421impl From<proto::ParamDataType> for ParamDataType {
422    fn from(data_type: proto::ParamDataType) -> Self {
423        match data_type {
424            proto::ParamDataType::String => ParamDataType::String,
425            proto::ParamDataType::Integer => ParamDataType::Integer,
426            proto::ParamDataType::Decimal => ParamDataType::Decimal,
427            proto::ParamDataType::Boolean => ParamDataType::Boolean,
428            proto::ParamDataType::Uuid => ParamDataType::Uuid,
429            proto::ParamDataType::Date => ParamDataType::Date,
430            proto::ParamDataType::Timestamp => ParamDataType::Timestamp,
431            proto::ParamDataType::Json => ParamDataType::Json,
432        }
433    }
434}
435
436impl TryFrom<proto::Transaction> for TransactionValues {
437    type Error = CalaLedgerOutboxClientError;
438    fn try_from(
439        proto::Transaction {
440            id,
441            version,
442            created_at,
443            modified_at,
444            journal_id,
445            tx_template_id,
446            entry_ids,
447            effective,
448            correlation_id,
449            external_id,
450            description,
451            metadata,
452        }: proto::Transaction,
453    ) -> Result<Self, Self::Error> {
454        let res = Self {
455            id: id.parse()?,
456            version,
457            created_at: created_at
458                .ok_or(CalaLedgerOutboxClientError::MissingField)?
459                .into(),
460            modified_at: modified_at
461                .ok_or(CalaLedgerOutboxClientError::MissingField)?
462                .into(),
463            journal_id: journal_id.parse()?,
464            tx_template_id: tx_template_id.parse()?,
465            entry_ids: entry_ids
466                .into_iter()
467                .map(|id| id.parse())
468                .collect::<Result<_, _>>()?,
469            effective: effective.parse()?,
470            correlation_id,
471            external_id,
472            description,
473            metadata: metadata.map(serde_json::to_value).transpose()?,
474        };
475        Ok(res)
476    }
477}
478
479impl TryFrom<proto::Entry> for EntryValues {
480    type Error = CalaLedgerOutboxClientError;
481    fn try_from(
482        proto::Entry {
483            id,
484            version,
485            journal_id,
486            transaction_id,
487            entry_type,
488            sequence,
489            account_id,
490            layer,
491            direction,
492            units,
493            currency,
494            description,
495            metadata,
496        }: proto::Entry,
497    ) -> Result<Self, Self::Error> {
498        let res = Self {
499            id: id.parse()?,
500            version,
501            journal_id: journal_id.parse()?,
502            transaction_id: transaction_id.parse()?,
503            account_id: account_id.parse()?,
504            entry_type,
505            sequence,
506            layer: proto::Layer::try_from(layer).map(Layer::from)?,
507            direction: proto::DebitOrCredit::try_from(direction).map(DebitOrCredit::from)?,
508            units: units.parse()?,
509            currency: currency.parse::<Currency>()?,
510            description,
511            metadata: metadata.map(serde_json::to_value).transpose()?,
512        };
513        Ok(res)
514    }
515}
516
517impl TryFrom<proto::Balance> for BalanceSnapshot {
518    type Error = CalaLedgerOutboxClientError;
519    fn try_from(
520        proto::Balance {
521            journal_id,
522            account_id,
523            currency,
524            version,
525            created_at,
526            modified_at,
527            entry_id,
528            settled,
529            pending,
530            encumbrance,
531        }: proto::Balance,
532    ) -> Result<Self, Self::Error> {
533        let res = Self {
534            journal_id: journal_id.parse()?,
535            account_id: account_id.parse()?,
536            currency: currency.parse()?,
537            version,
538            created_at: created_at
539                .ok_or(CalaLedgerOutboxClientError::MissingField)?
540                .into(),
541            modified_at: modified_at
542                .ok_or(CalaLedgerOutboxClientError::MissingField)?
543                .into(),
544            entry_id: entry_id.parse()?,
545            settled: BalanceAmount::try_from(
546                settled.ok_or(CalaLedgerOutboxClientError::MissingField)?,
547            )?,
548            pending: BalanceAmount::try_from(
549                pending.ok_or(CalaLedgerOutboxClientError::MissingField)?,
550            )?,
551            encumbrance: BalanceAmount::try_from(
552                encumbrance.ok_or(CalaLedgerOutboxClientError::MissingField)?,
553            )?,
554        };
555        Ok(res)
556    }
557}
558
559impl TryFrom<proto::BalanceAmount> for BalanceAmount {
560    type Error = CalaLedgerOutboxClientError;
561
562    fn try_from(amount: proto::BalanceAmount) -> Result<Self, Self::Error> {
563        let res = Self {
564            dr_balance: amount.dr_balance.parse()?,
565            cr_balance: amount.cr_balance.parse()?,
566            entry_id: amount.entry_id.parse()?,
567            modified_at: amount
568                .modified_at
569                .ok_or(CalaLedgerOutboxClientError::MissingField)?
570                .into(),
571        };
572        Ok(res)
573    }
574}
575
576impl From<proto::Layer> for Layer {
577    fn from(layer: proto::Layer) -> Self {
578        match layer {
579            proto::Layer::Settled => Layer::Settled,
580            proto::Layer::Pending => Layer::Pending,
581            proto::Layer::Encumbrance => Layer::Encumbrance,
582        }
583    }
584}