opencode-stats 1.3.6

A terminal dashboard for OpenCode usage statistics inspired by the /stats command in Claude Code
use chrono::{DateTime, Datelike, Duration, Local, NaiveDate, TimeZone};

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum TimeRange {
    #[default]
    All,
    Last30Days,
    Last7Days,
}

impl TimeRange {
    pub fn label(self) -> &'static str {
        match self {
            Self::All => "All time",
            Self::Last30Days => "Last 30 days",
            Self::Last7Days => "Last 7 days",
        }
    }

    pub fn cycle(self) -> Self {
        match self {
            Self::All => Self::Last7Days,
            Self::Last7Days => Self::Last30Days,
            Self::Last30Days => Self::All,
        }
    }

    pub fn from_shortcut(value: char) -> Option<Self> {
        match value {
            '1' => Some(Self::All),
            '2' => Some(Self::Last7Days),
            '3' => Some(Self::Last30Days),
            _ => None,
        }
    }

    pub fn start_date(self, today: NaiveDate) -> Option<NaiveDate> {
        match self {
            Self::All => None,
            Self::Last30Days => Some(today - Duration::days(29)),
            Self::Last7Days => Some(today - Duration::days(6)),
        }
    }
}

pub fn timestamp_ms_to_local(timestamp_ms: i64) -> Option<DateTime<Local>> {
    Local.timestamp_millis_opt(timestamp_ms).single()
}

pub fn current_local_date() -> NaiveDate {
    Local::now().date_naive()
}

pub fn custom_week_start(date: NaiveDate, week_start_day: u32) -> NaiveDate {
    let current_weekday = date.weekday().num_days_from_monday();
    let days_back = (7 + current_weekday as i64 - week_start_day as i64) % 7;
    date - Duration::days(days_back)
}

pub fn month_start(date: NaiveDate) -> NaiveDate {
    date.with_day(1).unwrap_or(date)
}

pub fn in_range(date: NaiveDate, range: TimeRange, today: NaiveDate) -> bool {
    match range.start_date(today) {
        Some(start) => date >= start && date <= today,
        None => date <= today,
    }
}

#[cfg(test)]
mod tests {
    use super::{TimeRange, custom_week_start};
    use chrono::NaiveDate;

    #[test]
    fn cycles_ranges() {
        assert_eq!(TimeRange::All.cycle(), TimeRange::Last7Days);
        assert_eq!(TimeRange::Last7Days.cycle(), TimeRange::Last30Days);
        assert_eq!(TimeRange::Last30Days.cycle(), TimeRange::All);
    }

    #[test]
    fn computes_custom_week_start() {
        let date = NaiveDate::from_ymd_opt(2026, 3, 10).unwrap();
        let sunday_start = custom_week_start(date, 6);
        assert_eq!(sunday_start, NaiveDate::from_ymd_opt(2026, 3, 8).unwrap());
    }
}