Skip to main content

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}