vct_core/models/aggregate.rs
1//! Neutral per-provider totals container shared by the `usage` and `analysis`
2//! roll-ups (and their display summaries).
3//!
4//! `ProviderTotals<S>` is pure data over [`Provider`] — no feature or
5//! presentation knowledge — so both core summaries and the display layer build
6//! and read it without either depending on the other.
7
8use crate::models::Provider;
9
10/// Per-provider totals organized by AI provider.
11///
12/// Keeps each provider's running totals alongside an `overall` "All Providers"
13/// bucket. The `S` parameter is the per-provider stats type each feature
14/// supplies (e.g. `ProviderStats` for usage, `AnalysisProviderStats` for
15/// analysis).
16pub struct ProviderTotals<S> {
17 /// Totals for Claude Code sessions.
18 pub claude: S,
19 /// Totals for OpenAI Codex sessions.
20 pub codex: S,
21 /// Totals for GitHub Copilot CLI sessions.
22 pub copilot: S,
23 /// Totals for Gemini CLI sessions.
24 pub gemini: S,
25 /// Totals for OpenCode sessions.
26 pub opencode: S,
27 /// Totals for Cursor sessions.
28 pub cursor: S,
29 /// Totals for Hermes sessions.
30 pub hermes: S,
31 /// Totals for Grok CLI sessions.
32 pub grok: S,
33 /// Sum across every provider (the "All Providers" bucket).
34 pub overall: S,
35}
36
37impl<S: Default> Default for ProviderTotals<S> {
38 fn default() -> Self {
39 Self {
40 claude: S::default(),
41 codex: S::default(),
42 copilot: S::default(),
43 gemini: S::default(),
44 opencode: S::default(),
45 cursor: S::default(),
46 hermes: S::default(),
47 grok: S::default(),
48 overall: S::default(),
49 }
50 }
51}
52
53impl<S> ProviderTotals<S> {
54 /// Borrows the stats bucket for `provider`.
55 ///
56 /// [`Provider::Unknown`] maps to the `overall` bucket, since there is no
57 /// dedicated slot for unclassified providers.
58 pub fn get_stats(&self, provider: Provider) -> &S {
59 match provider {
60 Provider::ClaudeCode => &self.claude,
61 Provider::Codex => &self.codex,
62 Provider::Copilot => &self.copilot,
63 Provider::Gemini => &self.gemini,
64 Provider::OpenCode => &self.opencode,
65 Provider::Cursor => &self.cursor,
66 Provider::Hermes => &self.hermes,
67 Provider::Grok => &self.grok,
68 Provider::Unknown => &self.overall,
69 }
70 }
71
72 /// Mutably borrows the stats bucket for `provider`.
73 ///
74 /// [`Provider::Unknown`] maps to the `overall` bucket, since there is no
75 /// dedicated slot for unclassified providers.
76 pub fn get_stats_mut(&mut self, provider: Provider) -> &mut S {
77 match provider {
78 Provider::ClaudeCode => &mut self.claude,
79 Provider::Codex => &mut self.codex,
80 Provider::Copilot => &mut self.copilot,
81 Provider::Gemini => &mut self.gemini,
82 Provider::OpenCode => &mut self.opencode,
83 Provider::Cursor => &mut self.cursor,
84 Provider::Hermes => &mut self.hermes,
85 Provider::Grok => &mut self.grok,
86 Provider::Unknown => &mut self.overall,
87 }
88 }
89}