use chrono::{Datelike, NaiveDate};
use serde::Deserialize;
const SPRING_TRAINING_FALLBACK_MONTH: u32 = 3;
const SPRING_TRAINING_FALLBACK_DAY: u32 = 20;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum GameType {
SpringTraining,
RegularSeason,
}
#[derive(Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct SeasonsResponse {
pub seasons: Vec<SeasonInfo>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SeasonInfo {
pub regular_season_start_date: NaiveDate,
}
pub fn game_type_for_date(date: NaiveDate, season_info: Option<&SeasonInfo>) -> GameType {
match season_info {
Some(info) => {
if date < info.regular_season_start_date {
GameType::SpringTraining
} else {
GameType::RegularSeason
}
}
None => {
let cutoff = NaiveDate::from_ymd_opt(
date.year(),
SPRING_TRAINING_FALLBACK_MONTH,
SPRING_TRAINING_FALLBACK_DAY,
);
match cutoff {
Some(c) if date < c => GameType::SpringTraining,
_ => GameType::RegularSeason,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spring_training_with_season_info() {
let info = SeasonInfo {
regular_season_start_date: NaiveDate::from_ymd_opt(2026, 3, 25).unwrap(),
};
let spring = NaiveDate::from_ymd_opt(2026, 3, 10).unwrap();
let regular = NaiveDate::from_ymd_opt(2026, 3, 25).unwrap();
let mid_season = NaiveDate::from_ymd_opt(2026, 7, 1).unwrap();
assert_eq!(
game_type_for_date(spring, Some(&info)),
GameType::SpringTraining
);
assert_eq!(
game_type_for_date(regular, Some(&info)),
GameType::RegularSeason
);
assert_eq!(
game_type_for_date(mid_season, Some(&info)),
GameType::RegularSeason
);
}
#[test]
fn test_spring_training_fallback_without_season_info() {
let before_cutoff = NaiveDate::from_ymd_opt(2026, 3, 1).unwrap();
let after_cutoff = NaiveDate::from_ymd_opt(2026, 3, 25).unwrap();
let on_cutoff = NaiveDate::from_ymd_opt(2026, 3, 20).unwrap();
assert_eq!(
game_type_for_date(before_cutoff, None),
GameType::SpringTraining
);
assert_eq!(
game_type_for_date(after_cutoff, None),
GameType::RegularSeason
);
assert_eq!(game_type_for_date(on_cutoff, None), GameType::RegularSeason);
}
}