kcode_k1_accounting/
lib.rs1use std::{
2 collections::BTreeMap,
3 sync::{Arc, Mutex, MutexGuard},
4};
5
6use rust_decimal::Decimal;
7
8#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
9pub struct UsageValue {
10 pub units: Decimal,
11 pub price_cents: Decimal,
12}
13
14#[derive(Clone, Debug, Default, Eq, PartialEq)]
15pub struct AccountingEvent {
16 pub source: String,
17 pub operation: String,
18 pub usage: BTreeMap<String, UsageValue>,
19}
20
21#[derive(Clone, Default)]
22pub struct Accounting {
23 ledger: Arc<Mutex<Ledger>>,
24}
25
26#[derive(Default)]
27struct Ledger {
28 events: Vec<Arc<AccountingEvent>>,
29}
30
31impl Accounting {
32 pub fn new() -> Self {
33 Self::default()
34 }
35
36 pub fn record(&self, event: &AccountingEvent) {
37 let event = Arc::new(event.clone());
38 self.lock().events.push(event);
39 }
40
41 pub fn entries(&self) -> Vec<AccountingEvent> {
42 let events = self.lock().events.clone();
43 events.into_iter().map(|event| (*event).clone()).collect()
44 }
45
46 pub fn totals(&self) -> BTreeMap<String, UsageValue> {
47 let events = self.lock().events.clone();
48 let mut totals: BTreeMap<String, UsageValue> = BTreeMap::new();
49
50 for event in events {
51 for (key, value) in &event.usage {
52 let total = totals.entry(key.clone()).or_default();
53 total.units += value.units;
54 total.price_cents += value.price_cents;
55 }
56 }
57
58 totals
59 }
60
61 fn lock(&self) -> MutexGuard<'_, Ledger> {
62 self.ledger
63 .lock()
64 .unwrap_or_else(|poisoned| poisoned.into_inner())
65 }
66}