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}