use clap::ValueEnum;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ValueEnum)]
pub enum AppKind {
Claude,
Codex,
#[value(name = "opencode")]
OpenCode,
Gemini,
Grok,
Pi,
Kimi,
}
impl AppKind {
pub const ALL: [AppKind; 7] = [
AppKind::Claude,
AppKind::Codex,
AppKind::OpenCode,
AppKind::Gemini,
AppKind::Grok,
AppKind::Pi,
AppKind::Kimi,
];
pub fn as_str(&self) -> &'static str {
match self {
AppKind::Claude => "claude",
AppKind::Codex => "codex",
AppKind::OpenCode => "opencode",
AppKind::Gemini => "gemini",
AppKind::Grok => "grok",
AppKind::Pi => "pi",
AppKind::Kimi => "kimi",
}
}
}
impl fmt::Display for AppKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct UsageEntry {
pub app: AppKind,
pub model: String,
#[allow(dead_code)]
pub session_id: Option<String>,
pub created_at: i64,
pub input_tokens: u64,
pub output_tokens: u64,
pub cache_read_tokens: u64,
pub cache_creation_tokens: u64,
pub self_cost_usd: Option<f64>,
pub cost_usd: Option<f64>,
}
impl UsageEntry {
#[allow(clippy::too_many_arguments)]
pub fn new(
app: AppKind,
model: String,
session_id: Option<String>,
created_at: i64,
input_tokens: u64,
output_tokens: u64,
cache_read_tokens: u64,
cache_creation_tokens: u64,
self_cost_usd: Option<f64>,
) -> Self {
Self {
app,
model,
session_id,
created_at,
input_tokens,
output_tokens,
cache_read_tokens,
cache_creation_tokens,
self_cost_usd,
cost_usd: None,
}
}
#[cfg(test)]
pub fn total_tokens(&self) -> u64 {
self.input_tokens + self.output_tokens + self.cache_read_tokens + self.cache_creation_tokens
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq)]
pub struct TokenTotals {
pub requests: u64,
pub input_tokens: u64,
pub output_tokens: u64,
pub cache_read_tokens: u64,
pub cache_creation_tokens: u64,
pub cost_usd: f64,
pub unpriced: u64,
}
impl TokenTotals {
pub fn add_entry(&mut self, entry: &UsageEntry) {
self.requests += 1;
self.input_tokens += entry.input_tokens;
self.output_tokens += entry.output_tokens;
self.cache_read_tokens += entry.cache_read_tokens;
self.cache_creation_tokens += entry.cache_creation_tokens;
match entry.cost_usd {
Some(cost) => self.cost_usd += cost,
None => self.unpriced += 1,
}
}
pub fn total_tokens(&self) -> u64 {
self.input_tokens + self.output_tokens + self.cache_read_tokens + self.cache_creation_tokens
}
}