use serde_json::Value;
use std::{collections::HashMap, path::Path};
use crate::{
apps::fresh_input,
error::AppError,
io::load,
model::{AppKind, UsageEntry},
};
const MAX_DEPTH: usize = 3;
pub fn collect() -> Result<Vec<UsageEntry>, AppError> {
let base = load::home_dir()?.join(".gemini").join("tmp");
if !base.is_dir() {
return Ok(Vec::new());
}
collect_from(&base)
}
pub fn collect_from(base: &Path) -> Result<Vec<UsageEntry>, AppError> {
let mut candidates: HashMap<String, UsageEntry> = HashMap::new();
for file in load::discover_files(base, "json", MAX_DEPTH) {
let name = file.file_name().and_then(|n| n.to_str()).unwrap_or("");
if !name.starts_with("session-") || !name.ends_with(".json") {
continue;
}
let Ok(value) = load::read_json(&file) else {
continue;
};
parse_session(&value, &mut candidates);
}
Ok(candidates.into_values().collect())
}
fn parse_session(value: &Value, candidates: &mut HashMap<String, UsageEntry>) {
let session_id = load::str_get(value, &["sessionId"]).map(str::to_string);
let Some(messages) = value.get("messages").and_then(Value::as_array) else {
return;
};
for msg in messages {
if load::str_get(msg, &["type"]) != Some("gemini") {
continue;
}
let input = load::u64_get(msg, &["tokens", "input"]);
let output = load::u64_get(msg, &["tokens", "output"]);
let cached = load::u64_get(msg, &["tokens", "cached"]);
let thoughts = load::u64_get(msg, &["tokens", "thoughts"]);
if input == 0 && output == 0 && cached == 0 && thoughts == 0 {
continue;
}
let input = fresh_input(input, cached, 0);
let msg_id = load::str_get(msg, &["id"]).unwrap_or("unknown");
let dedup_key = format!("{}:{msg_id}", session_id.as_deref().unwrap_or("unknown"));
let model = load::str_get(msg, &["model"])
.unwrap_or("unknown")
.to_string();
let created_at = msg
.get("timestamp")
.and_then(load::timestamp_to_epoch)
.unwrap_or_else(load::now_epoch);
candidates.insert(
dedup_key,
UsageEntry::new(
AppKind::Gemini,
model,
session_id.clone(),
created_at,
input,
output + thoughts,
cached,
0,
None,
),
);
}
}
#[cfg(test)]
#[path = "tests/gemini_test.rs"]
mod tests;