use crate::harnesses::Harness;
fn today_utc() -> String {
let secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let z = secs / 86400;
let z = z + 719468;
let era = z / 146097;
let doe = z - era * 146097;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let mo = if mp < 10 { mp + 3 } else { mp - 9 };
let y = if mo <= 2 { y + 1 } else { y };
format!("{:04}-{:02}-{:02}", y, mo, d)
}
pub fn run(backfill: bool, selection: Option<&'static dyn Harness>, quiet: bool) {
let today = today_utc();
let single: [&'static dyn Harness; 1];
let harnesses: &[&'static dyn Harness] = match selection {
Some(h) => {
single = [h];
&single
}
None => crate::harnesses::all(),
};
if !backfill {
crate::sync::run_harnesses(&today, &today, harnesses);
if !quiet {
println!("vibestats: sync complete");
}
} else {
let mut activities = std::collections::HashMap::new();
for harness in harnesses {
let harness_activities = harness.parse_date_range("0000-00-00", &today);
for date in harness_activities.keys() {
activities.insert(date.clone(), ());
}
}
if activities.is_empty() {
if !quiet {
println!("vibestats: backfill complete — no local data found");
}
return;
}
let mut dates: Vec<&String> = activities.keys().collect();
dates.sort();
let earliest = dates[0].clone();
let count = dates.len();
crate::sync::run_harnesses(&earliest, &today, harnesses);
if !quiet {
println!("vibestats: backfill complete — processed {} date(s)", count);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn today_utc_has_correct_format() {
let date = today_utc();
assert_eq!(date.len(), 10, "date must be 10 chars: got '{}'", date);
assert_eq!(
&date[4..5],
"-",
"expected '-' at position 4, got '{}'",
date
);
assert_eq!(
&date[7..8],
"-",
"expected '-' at position 7, got '{}'",
date
);
let digits_only: String = date.chars().filter(|c| *c != '-').collect();
assert!(
digits_only.chars().all(|c| c.is_ascii_digit()),
"non-digit characters in date: '{}'",
date
);
}
#[test]
fn today_utc_year_is_at_least_2026() {
let date = today_utc();
let year: u32 = date[0..4].parse().expect("year portion must be a number");
assert!(
year >= 2026,
"year {} is before 2026 — likely an epoch bug",
year
);
}
}