1pub mod doctor;
10pub mod ingest;
11pub mod purge;
12pub mod query;
13pub mod report;
14pub mod suggest;
15pub mod watch;
16
17use std::io;
18
19use crate::cli::TimeWindow;
20use crate::config::Config;
21use crate::reports::ReportCtx;
22use crate::store::StorePaths;
23
24#[derive(Debug, Clone, Copy)]
29pub struct Env<'a> {
30 pub config: &'a Config,
31 pub paths: &'a StorePaths,
32 pub window: TimeWindow,
33 pub project: Option<&'a str>,
34 pub json: bool,
35 pub no_ingest: bool,
36 pub include_sidechain: bool,
38}
39
40impl Env<'_> {
41 pub fn ctx(&self) -> ReportCtx {
45 ReportCtx::new(
46 self.window,
47 self.project.map(str::to_string),
48 self.include_sidechain,
49 )
50 .with_pricing(self.config.pricing())
51 }
52
53 pub fn pre_ingest(&self) -> io::Result<()> {
58 if self.no_ingest {
59 return Ok(());
60 }
61 let report = ingest::run_quiet(self.config, self.paths, self.window, self.project)?;
62 ingest::write_lines(&mut io::stderr(), &report)
63 }
64}
65
66pub(crate) fn thousands(n: u64) -> String {
68 let digits = n.to_string();
69 let mut out = String::with_capacity(digits.len() + digits.len() / 3);
70 for (i, ch) in digits.chars().enumerate() {
71 if i > 0 && (digits.len() - i).is_multiple_of(3) {
72 out.push(',');
73 }
74 out.push(ch);
75 }
76 out
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn groups_digits_in_threes() {
85 assert_eq!(thousands(0), "0");
86 assert_eq!(thousands(999), "999");
87 assert_eq!(thousands(1_203), "1,203");
88 assert_eq!(thousands(1_000_000), "1,000,000");
89 }
90}