Skip to main content

simulator_api/
usage.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4/// Response body of `GET /usage`.
5#[derive(Debug, Serialize, Deserialize)]
6pub struct UsageReport {
7    pub api_key_name: String,
8    pub since: DateTime<Utc>,
9    pub until: DateTime<Utc>,
10    pub sessions: SessionCounts,
11    pub compute: ComputeTotals,
12}
13
14/// Raw `backtest_session_*` point counts for this api key in the window, by
15/// outcome — not session-deduplicated, so the three need not balance (a
16/// pre-start failure has no `started`; a cross-manager handoff re-emits
17/// `started`). Billing uses the compute totals, which derive only from the
18/// terminal completed/failed points.
19#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
20pub struct SessionCounts {
21    pub started: u64,
22    pub completed: u64,
23    pub failed: u64,
24}
25
26/// Aggregated compute totals for a usage window.
27#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
28pub struct ComputeTotals {
29    pub executed_slot_count: u64,
30    pub session_duration_ms: u64,
31}
32
33impl SessionCounts {
34    pub fn saturating_add(self, other: Self) -> Self {
35        Self {
36            started: self.started.saturating_add(other.started),
37            completed: self.completed.saturating_add(other.completed),
38            failed: self.failed.saturating_add(other.failed),
39        }
40    }
41}
42
43impl ComputeTotals {
44    pub fn saturating_add(self, other: Self) -> Self {
45        Self {
46            executed_slot_count: self
47                .executed_slot_count
48                .saturating_add(other.executed_slot_count),
49            session_duration_ms: self
50                .session_duration_ms
51                .saturating_add(other.session_duration_ms),
52        }
53    }
54}