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}