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}