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        };
273        Ok(res)
274    }
275}
276
277impl From<proto::DebitOrCredit> for DebitOrCredit {
278    fn from(dc: proto::DebitOrCredit) -> Self {
279        match dc {
280            proto::DebitOrCredit::Debit => DebitOrCredit::Debit,
281            proto::DebitOrCredit::Credit => DebitOrCredit::Credit,
282        }
283    }
284}
285
286impl From<proto::Status> for Status {
287    fn from(status: proto::Status) -> Self {
288        match status {
289            proto::Status::Active => Status::Active,
290            proto::Status::Locked => Status::Locked,
291        }
292    }
293}
294
295impl TryFrom<proto::TxTemplate> for TxTemplateValues {
296    type Error = CalaLedgerOutboxClientError;
297
298    fn try_from(
299        proto::TxTemplate {
300            id,
301            version,
302            code,
303            params,
304            transaction,
305            entries,
306            description,
307            metadata,
308        }: proto::TxTemplate,
309    ) -> Result<Self, Self::Error> {
310        let params = params
311            .into_iter()
312            .map(ParamDefinition::try_from)
313            .collect::<Result<Vec<_>, _>>()?;
314        let transaction = TxTemplateTransaction::try_from(
315            transaction.ok_or(CalaLedgerOutboxClientError::MissingField)?,
316        )?;
317        let entries = entries
318            .into_iter()
319            .map(TxTemplateEntry::try_from)
320            .collect::<Result<Vec<_>, _>>()?;
321
322        let res = Self {
323            id: id.parse()?,
324            version,
325            code,
326            params: Some(params),
327            transaction,
328            entries,
329            description,
330            metadata: metadata.map(serde_json::to_value).transpose()?,
331        };
332        Ok(res)
333    }
334}
335
336impl TryFrom<proto::ParamDefinition> for ParamDefinition {
337    type Error = CalaLedgerOutboxClientError;
338    fn try_from(
339        proto::ParamDefinition {
340            name,
341            data_type,
342            default,
343            description,
344        }: proto::ParamDefinition,
345    ) -> Result<Self, Self::Error> {
346        let res = Self {
347            name,
348            r#type: proto::ParamDataType::try_from(data_type).map(ParamDataType::from)?,
349            default: default.map(CelExpression::try_from).transpose()?,
350            description,
351        };
352        Ok(res)
353    }
354}
355
356impl TryFrom<proto::TxTemplateTransaction> for TxTemplateTransaction {
357    type Error = CalaLedgerOutboxClientError;
358    fn try_from(
359        proto::TxTemplateTransaction {
360            effective,
361            journal_id,
362            correlation_id,
363            external_id,
364            description,
365            metadata,
366        }: proto::TxTemplateTransaction,
367    ) -> Result<Self, Self::Error> {
368        let res = Self {
369            effective: CelExpression::try_from(effective)?,
370            journal_id: CelExpression::try_from(journal_id)?,
371            correlation_id: correlation_id.map(CelExpression::try_from).transpose()?,
372            external_id: external_id.map(CelExpression::try_from).transpose()?,
373            description: description.map(CelExpression::try_from).transpose()?,
374            metadata: metadata.map(CelExpression::try_from).transpose()?,
375        };
376        Ok(res)
377    }
378}
379
380impl TryFrom<proto::TxTemplateEntry> for TxTemplateEntry {
381    type Error = CalaLedgerOutboxClientError;
382    fn try_from(
383        proto::TxTemplateEntry {
384            entry_type,
385            account_id,
386            layer,
387            direction,
388            units,
389            currency,
390            description,
391            metadata,
392        }: proto::TxTemplateEntry,
393    ) -> Result<Self, Self::Error> {
394        let res = Self {
395            entry_type: CelExpression::try_from(entry_type)?,
396            account_id: CelExpression::try_from(account_id)?,
397            layer: CelExpression::try_from(layer)?,
398            direction: CelExpression::try_from(direction)?,
399            units: CelExpression::try_from(units)?,
400            currency: CelExpression::try_from(currency)?,
401            description: description.map(CelExpression::try_from).transpose()?,
402            metadata: metadata.map(CelExpression::try_from).transpose()?,
403        };
404        Ok(res)
405    }
406}
407
408impl From<proto::ParamDataType> for ParamDataType {
409    fn from(data_type: proto::ParamDataType) -> Self {
410        match data_type {
411            proto::ParamDataType::String => ParamDataType::String,
412            proto::ParamDataType::Integer => ParamDataType::Integer,
413            proto::ParamDataType::Decimal => ParamDataType::Decimal,
414            proto::ParamDataType::Boolean => ParamDataType::Boolean,
415            proto::ParamDataType::Uuid => ParamDataType::Uuid,
416            proto::ParamDataType::Date => ParamDataType::Date,
417            proto::ParamDataType::Timestamp => ParamDataType::Timestamp,
418            proto::ParamDataType::Json => ParamDataType::Json,
419        }
420    }
421}
422
423impl TryFrom<proto::Transaction> for TransactionValues {
424    type Error = CalaLedgerOutboxClientError;
425    fn try_from(
426        proto::Transaction {
427            id,
428            version,
429            created_at,
430            modified_at,
431            journal_id,
432            tx_template_id,
433            entry_ids,
434            effective,
435            correlation_id,
436            external_id,
437            description,
438            metadata,
439        }: proto::Transaction,
440    ) -> Result<Self, Self::Error> {
441        let res = Self {
442            id: id.parse()?,
443            version,
444            created_at: created_at
445                .ok_or(CalaLedgerOutboxClientError::MissingField)?
446                .into(),
447            modified_at: modified_at
448                .ok_or(CalaLedgerOutboxClientError::MissingField)?
449                .into(),
450            journal_id: journal_id.parse()?,
451            tx_template_id: tx_template_id.parse()?,
452            entry_ids: entry_ids
453                .into_iter()
454                .map(|id| id.parse())
455                .collect::<Result<_, _>>()?,
456            effective: effective.parse()?,
457            correlation_id,
458            external_id,
459            description: description.map(String::from),
460            metadata: metadata.map(serde_json::to_value).transpose()?,
461        };
462        Ok(res)
463    }
464}
465
466impl TryFrom<proto::Entry> for EntryValues {
467    type Error = CalaLedgerOutboxClientError;
468    fn try_from(
469        proto::Entry {
470            id,
471            version,
472            journal_id,
473            transaction_id,
474            entry_type,
475            sequence,
476            account_id,
477            layer,
478            direction,
479            units,
480            currency,
481            description,
482            metadata,
483        }: proto::Entry,
484    ) -> Result<Self, Self::Error> {
485        let res = Self {
486            id: id.parse()?,
487            version,
488            journal_id: journal_id.parse()?,
489            transaction_id: transaction_id.parse()?,
490            account_id: account_id.parse()?,
491            entry_type,
492            sequence,
493            layer: proto::Layer::try_from(layer).map(Layer::from)?,
494            direction: proto::DebitOrCredit::try_from(direction).map(DebitOrCredit::from)?,
495            units: units.parse()?,
496            currency: currency.parse::<Currency>()?,
497            description: description.map(String::from),
498            metadata: metadata.map(serde_json::to_value).transpose()?,
499        };
500        Ok(res)
501    }
502}
503
504impl TryFrom<proto::Balance> for BalanceSnapshot {
505    type Error = CalaLedgerOutboxClientError;
506    fn try_from(
507        proto::Balance {
508            journal_id,
509            account_id,
510            currency,
511            version,
512            created_at,
513            modified_at,
514            entry_id,
515            settled,
516            pending,
517            encumbrance,
518        }: proto::Balance,
519    ) -> Result<Self, Self::Error> {
520        let res = Self {
521            journal_id: journal_id.parse()?,
522            account_id: account_id.parse()?,
523            currency: currency.parse()?,
524            version,
525            created_at: created_at
526                .ok_or(CalaLedgerOutboxClientError::MissingField)?
527                .into(),
528            modified_at: modified_at
529                .ok_or(CalaLedgerOutboxClientError::MissingField)?
530                .into(),
531            entry_id: entry_id.parse()?,
532            settled: BalanceAmount::try_from(
533                settled.ok_or(CalaLedgerOutboxClientError::MissingField)?,
534            )?,
535            pending: BalanceAmount::try_from(
536                pending.ok_or(CalaLedgerOutboxClientError::MissingField)?,
537            )?,
538            encumbrance: BalanceAmount::try_from(
539                encumbrance.ok_or(CalaLedgerOutboxClientError::MissingField)?,
540            )?,
541        };
542        Ok(res)
543    }
544}
545
546impl TryFrom<proto::BalanceAmount> for BalanceAmount {
547    type Error = CalaLedgerOutboxClientError;
548
549    fn try_from(amount: proto::BalanceAmount) -> Result<Self, Self::Error> {
550        let res = Self {
551            dr_balance: amount.dr_balance.parse()?,
552            cr_balance: amount.cr_balance.parse()?,
553            entry_id: amount.entry_id.parse()?,
554            modified_at: amount
555                .modified_at
556                .ok_or(CalaLedgerOutboxClientError::MissingField)?
557                .into(),
558        };
559        Ok(res)
560    }
561}
562
563impl From<proto::Layer> for Layer {
564    fn from(layer: proto::Layer) -> Self {
565        match layer {
566            proto::Layer::Settled => Layer::Settled,
567            proto::Layer::Pending => Layer::Pending,
568            proto::Layer::Encumbrance => Layer::Encumbrance,
569        }
570    }
571}