Files
rota/src-tauri/src/slot.rs

295 lines
7.9 KiB
Rust

use chrono::{Datelike, NaiveDate, Weekday};
use serde::{Deserialize, Serialize};
use crate::schedule::ShiftType;
#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Ord, Eq, Debug, Hash, Clone, Copy)]
#[serde(rename_all = "camelCase")]
pub struct Slot {
pub day: Day,
pub position: ShiftPosition,
}
impl Slot {
pub fn new(day: Day, position: ShiftPosition) -> Self {
Self { day, position }
}
pub fn is_open_shift(&self) -> bool {
self.day.is_open_shift()
}
pub fn is_first(&self) -> bool {
self.day == Day(1) && self.position == ShiftPosition::First
}
pub fn is_first_day(&self) -> bool {
self.day == Day(1)
}
pub fn is_open_first(&self) -> bool {
self.is_open_shift() && self.position == ShiftPosition::First
}
pub fn is_open_second(&self) -> bool {
self.is_open_shift() && self.position == ShiftPosition::Second
}
pub fn shift_type_str(&self) -> String {
match (self.day.is_open_shift(), self.position) {
(true, ShiftPosition::First) => "Ανοιχτή(1)".to_string(),
(true, ShiftPosition::Second) => "Ανοιχτή(2)".to_string(),
_ => "Κλειστή".to_string(),
}
}
pub fn next(&self) -> Self {
match self.position {
ShiftPosition::First if self.is_open_shift() => Self {
day: self.day,
position: ShiftPosition::Second,
},
ShiftPosition::First | ShiftPosition::Second => Self {
day: self.day.next(),
position: ShiftPosition::First,
},
}
}
pub fn previous(&self) -> Self {
match self.position {
ShiftPosition::First => {
let past_day = self.day.previous();
if past_day.is_open_shift() {
Self {
day: past_day,
position: ShiftPosition::Second,
}
} else {
Self {
day: past_day,
position: ShiftPosition::First,
}
}
}
ShiftPosition::Second => Self {
day: self.day,
position: ShiftPosition::First,
},
}
}
pub fn greater_than(&self, limit: u8) -> bool {
self.day.greater_than(&Day(limit))
}
pub fn other_position(&self) -> Option<Self> {
if !self.day.is_open_shift() {
return None;
}
let other_pos = match self.position {
ShiftPosition::First => ShiftPosition::Second,
ShiftPosition::Second => ShiftPosition::First,
};
Some(Self {
day: self.day,
position: other_pos,
})
}
pub fn shift_type(&self) -> ShiftType {
match (self.is_open_shift(), self.position) {
(true, ShiftPosition::First) => ShiftType::OpenFirst,
(true, ShiftPosition::Second) => ShiftType::OpenSecond,
(false, _) => ShiftType::Closed,
}
}
}
impl Default for Slot {
fn default() -> Self {
Self {
day: Day(1),
position: ShiftPosition::First,
}
}
}
impl From<u8> for Slot {
fn from(value: u8) -> Self {
let mut slot = Slot::default();
for _ in 0..value {
slot = slot.next()
}
slot
}
}
#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Ord, Eq, Debug, Hash, Clone, Copy)]
#[serde(rename_all = "camelCase")]
pub struct Day(pub u8);
impl Day {
pub fn is_open_shift(&self) -> bool {
!self.0.is_multiple_of(2)
}
pub fn shift_types(&self) -> &[ShiftType] {
if self.is_open_shift() {
&[ShiftType::OpenFirst, ShiftType::OpenSecond]
} else {
&[ShiftType::Closed]
}
}
pub fn next(&self) -> Self {
Self(self.0 + 1)
}
pub fn previous(&self) -> Self {
if self.0 <= 1 {
Self(1)
} else {
Self(self.0 - 1)
}
}
pub fn greater_than(&self, other: &Day) -> bool {
self.0 > other.0
}
pub fn is_weekend(&self, month: u32, year: i32) -> bool {
let date = NaiveDate::from_ymd_opt(year, month, self.0 as u32);
match date {
Some(date) => {
let weekday = date.weekday();
weekday == Weekday::Sat || weekday == Weekday::Sun
}
None => false,
}
}
pub fn weekday_to_greek(&self, month: u32, year: i32) -> &'static str {
let date = NaiveDate::from_ymd_opt(year, month, self.0 as u32).unwrap();
match date.weekday() {
Weekday::Mon => "Δευτέρα",
Weekday::Tue => "Τρίτη",
Weekday::Wed => "Τετάρτη",
Weekday::Thu => "Πέμπτη",
Weekday::Fri => "Παρασκευή",
Weekday::Sat => "Σάββατο",
Weekday::Sun => "Κυριακή",
}
}
}
#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Ord, Eq, Debug, Hash, Clone, Copy)]
pub enum ShiftPosition {
First,
Second,
}
impl ShiftPosition {
pub fn as_str(self) -> &'static str {
match self {
ShiftPosition::First => "First",
ShiftPosition::Second => "Second",
}
}
}
impl TryFrom<&str> for ShiftPosition {
type Error = ();
fn try_from(s: &str) -> Result<Self, Self::Error> {
match s {
"First" => Ok(ShiftPosition::First),
"Second" => Ok(ShiftPosition::Second),
_ => Err(()),
}
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use crate::{
schedule::ShiftType,
slot::{Day, ShiftPosition, Slot},
};
#[rstest]
fn test_slot() {
let slot_1 = Slot::new(Day(1), ShiftPosition::First);
let slot_2 = Slot::new(Day(1), ShiftPosition::Second);
let slot_3 = Slot::new(Day(2), ShiftPosition::First);
assert!(slot_1.is_open_shift());
assert!(slot_2.is_open_shift());
assert!(!slot_3.is_open_shift());
assert!(slot_1.is_first());
assert!(!slot_2.is_first());
assert!(!slot_3.is_first());
assert!(slot_1.is_first_day());
assert!(slot_2.is_first_day());
assert!(!slot_3.is_first_day());
assert!(slot_1.is_open_first());
assert!(!slot_2.is_open_first());
assert!(!slot_3.is_open_first());
assert!(!slot_1.is_open_second());
assert!(slot_2.is_open_second());
assert!(!slot_3.is_open_second());
assert_eq!(slot_1.next(), slot_2);
assert_eq!(slot_2.next(), slot_3);
assert_eq!(slot_3.previous(), slot_2);
assert_eq!(slot_2.previous(), slot_1);
assert!(!slot_1.greater_than(1));
assert!(!slot_2.greater_than(1));
assert!(slot_3.greater_than(1));
assert_eq!(slot_1.other_position(), Some(slot_1.next()));
assert_eq!(slot_2.other_position(), Some(slot_2.previous()));
assert_eq!(slot_3.other_position(), None);
assert_eq!(slot_1.shift_type(), ShiftType::OpenFirst);
assert_eq!(slot_2.shift_type(), ShiftType::OpenSecond);
assert_eq!(slot_3.shift_type(), ShiftType::Closed);
}
#[rstest]
fn test_day() {
let day_1 = Day(1);
let day_2 = Day(2);
let day_3 = Day(3);
assert!(day_1.is_open_shift());
assert!(!day_2.is_open_shift());
assert!(day_3.is_open_shift());
assert_eq!(day_1.next(), day_2);
assert_eq!(day_2.next(), day_3);
assert_eq!(day_3.previous(), day_2);
assert_eq!(day_2.previous(), day_1);
assert!(!day_1.greater_than(&day_1));
assert!(day_2.greater_than(&day_1));
assert!(day_3.greater_than(&day_1));
assert!(day_1.is_weekend(2, 2026));
assert!(!day_2.is_weekend(2, 2026));
assert!(!day_3.is_weekend(2, 2026));
}
}