use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ShiftType {
Morning,
Afternoon,
Rest,
Night,
Study,
}
impl ShiftType {
pub fn label(&self) -> &'static str {
match self {
ShiftType::Morning => "早",
ShiftType::Afternoon => "中",
ShiftType::Rest => "休",
ShiftType::Night => "夜",
ShiftType::Study => "学",
}
}
pub fn full_label(&self) -> &'static str {
match self {
ShiftType::Morning => "早班",
ShiftType::Afternoon => "中班",
ShiftType::Rest => "休班",
ShiftType::Night => "夜班",
ShiftType::Study => "学习班",
}
}
pub fn label_en(&self) -> &'static str {
match self {
ShiftType::Morning => "AM",
ShiftType::Afternoon => "PM",
ShiftType::Rest => "R ",
ShiftType::Night => "NT",
ShiftType::Study => "TR",
}
}
pub fn label_en_padded(&self) -> &'static str {
match self {
ShiftType::Morning => "AM ",
ShiftType::Afternoon => "PM ",
ShiftType::Rest => "R ",
ShiftType::Night => "NT ",
ShiftType::Study => "TR ",
}
}
pub fn full_label_en(&self) -> &'static str {
match self {
ShiftType::Morning => "Morning",
ShiftType::Afternoon => "Afternoon",
ShiftType::Rest => "Rest",
ShiftType::Night => "Night",
ShiftType::Study => "Study",
}
}
pub fn is_work(&self) -> bool {
matches!(self, ShiftType::Morning | ShiftType::Afternoon | ShiftType::Night)
}
pub fn is_rest(&self) -> bool {
matches!(self, ShiftType::Rest | ShiftType::Study)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShiftInfo {
pub date: chrono::NaiveDate,
pub day_of_cycle: u32,
pub cycle_index: u32,
pub shift_type: ShiftType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShiftCycleConfig {
pub cycle: Vec<ShiftType>,
pub cycle_length: u32,
pub reference_date: chrono::NaiveDate,
pub total_teams: u32,
}
pub fn team_name(id: u32) -> String {
let prefix = match id {
1 => "一", 2 => "二", 3 => "三",
4 => "四", 5 => "五", 6 => "六",
_ => return format!("{}值", id),
};
format!("{}值", prefix)
}
impl ShiftCycleConfig {
pub fn new(cycle: Vec<ShiftType>, reference_date: chrono::NaiveDate, total_teams: u32) -> Self {
let cycle_length = cycle.len() as u32;
assert!(cycle_length >= 1, "cycle must be non-empty");
assert!(total_teams >= 1, "total_teams must be >= 1");
Self { cycle, cycle_length, reference_date, total_teams }
}
pub fn team_phase_offset(&self, team_id: u32) -> u32 {
(team_id - 1) * (self.cycle_length / self.total_teams)
}
}