1pub mod features;
25pub mod utils;
26
27pub use features::currency::{find as currency_by_code, Currency, CURRENCIES};
28pub use features::report::{
29 axis_label, axis_tokens, bucket_at, bucket_label_of, build_report, by_project, chart_bucket,
30 drill, nice_max, pie, project_key, short_label, short_name, slice_angles, time_series, Bucket,
31 Drill, Metric, Pie, Query, ReportRow, RowFilter, Selection, Slice, TimeSeries, ToolFilter,
32 Window,
33};
34pub use features::settings::{self, Settings};
35pub use features::theme::{
36 find_theme, theme_by_index, theme_index_by_name, AppTheme, Rgb, THEME_COUNT, THEME_NAMES,
37};
38pub use features::usage::{Row, Summary, Tool};
39pub use utils::layout::LayoutClass;
40
41use chrono::{DateTime, Utc};
42
43#[derive(Debug, Default, Clone)]
45pub struct Collected {
46 pub rows: Vec<Row>,
48 pub warnings: Vec<String>,
50}
51
52pub fn collect(tool: ToolFilter, start: DateTime<Utc>) -> Collected {
59 use features::{amp, claude, codex, copilot, gemini, opencode, pi, zed};
60
61 type Job = fn(DateTime<Utc>) -> anyhow::Result<Vec<Row>>;
62 let jobs: [(Tool, Job); 8] = [
63 (Tool::Pi, pi::collect_pi),
64 (Tool::Zed, zed::collect_zed),
65 (Tool::Claude, claude::collect_claude),
66 (Tool::Codex, codex::collect_codex),
67 (Tool::Copilot, copilot::collect_copilot),
68 (Tool::Gemini, gemini::collect_gemini),
69 (Tool::OpenCode, opencode::collect_opencode),
70 (Tool::Amp, amp::collect_amp),
71 ];
72
73 let results: Vec<(Tool, anyhow::Result<Vec<Row>>)> = std::thread::scope(|scope| {
74 let handles: Vec<_> = jobs
75 .iter()
76 .filter(|(t, _)| tool.includes(*t))
77 .map(|(t, job)| (*t, scope.spawn(move || job(start))))
78 .collect();
79 handles
80 .into_iter()
81 .map(|(t, h)| {
82 let r = h
83 .join()
84 .unwrap_or_else(|_| Err(anyhow::anyhow!("collector panicked")));
85 (t, r)
86 })
87 .collect()
88 });
89
90 let mut out = Collected::default();
91 for (t, r) in results {
92 match r {
93 Ok(rows) => out.rows.extend(rows),
94 Err(e) => out.warnings.push(format!("{}: {e:#}", t.as_str())),
95 }
96 }
97 out.rows.retain(|r| r.ts >= start);
98 out.rows.sort_by_key(|r| r.ts);
99 out
100}