#[cfg(test)]
mod test_day_of_week;
use std::fmt;
use crate::{
into_caveat, json,
warning::{self, GatherWarnings as _, IntoCaveat as _},
OutOfRange, Verdict,
};
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Warning {
ContainsEscapeCodes,
Decode(json::decode::Warning),
PreferUpperCase,
InvalidDay,
InvalidType { type_found: json::ValueKind },
}
impl Warning {
fn invalid_type(elem: &json::Element<'_>) -> Self {
Self::InvalidType {
type_found: elem.value().kind(),
}
}
}
impl fmt::Display for Warning {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ContainsEscapeCodes => write!(
f,
"The value contains escape codes but it does not need them."
),
Self::Decode(warning) => fmt::Display::fmt(warning, f),
Self::PreferUpperCase => write!(f, "The day should be uppercase."),
Self::InvalidDay => {
write!(f, "The value is not a valid day.")
}
Self::InvalidType { type_found } => {
write!(f, "The value should be a string but is `{type_found}`")
}
}
}
}
impl crate::Warning for Warning {
fn id(&self) -> warning::Id {
match self {
Self::ContainsEscapeCodes => warning::Id::from_static("contains_escape_codes"),
Self::Decode(warning) => warning.id(),
Self::PreferUpperCase => warning::Id::from_static("prefer_upper_case"),
Self::InvalidDay => warning::Id::from_static("invalid_day"),
Self::InvalidType { .. } => warning::Id::from_static("invalid_type"),
}
}
}
impl From<json::decode::Warning> for Warning {
fn from(warning: json::decode::Warning) -> Self {
Self::Decode(warning)
}
}
#[derive(Copy, Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Hash)]
pub(crate) enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
into_caveat!(Weekday);
impl From<chrono::Weekday> for Weekday {
fn from(value: chrono::Weekday) -> Self {
match value {
chrono::Weekday::Mon => Weekday::Monday,
chrono::Weekday::Tue => Weekday::Tuesday,
chrono::Weekday::Wed => Weekday::Wednesday,
chrono::Weekday::Thu => Weekday::Thursday,
chrono::Weekday::Fri => Weekday::Friday,
chrono::Weekday::Sat => Weekday::Saturday,
chrono::Weekday::Sun => Weekday::Sunday,
}
}
}
impl From<Weekday> for usize {
fn from(value: Weekday) -> Self {
match value {
Weekday::Monday => 0,
Weekday::Tuesday => 1,
Weekday::Wednesday => 2,
Weekday::Thursday => 3,
Weekday::Friday => 4,
Weekday::Saturday => 5,
Weekday::Sunday => 6,
}
}
}
impl TryFrom<usize> for Weekday {
type Error = OutOfRange;
fn try_from(value: usize) -> Result<Self, Self::Error> {
let day = match value {
0 => Weekday::Monday,
1 => Weekday::Tuesday,
2 => Weekday::Wednesday,
3 => Weekday::Thursday,
4 => Weekday::Friday,
5 => Weekday::Saturday,
6 => Weekday::Sunday,
_ => return Err(OutOfRange::new()),
};
Ok(day)
}
}
impl json::FromJson<'_, '_> for Weekday {
type Warning = Warning;
fn from_json(elem: &json::Element<'_>) -> Verdict<Self, Self::Warning> {
const NUM_DAYS: usize = 7;
const DAYS: [&str; NUM_DAYS] = [
"MONDAY",
"TUESDAY",
"WEDNESDAY",
"THURSDAY",
"FRIDAY",
"SATURDAY",
"SUNDAY",
];
let mut warnings = warning::Set::new();
let value = elem.as_value();
let Some(s) = value.as_raw_str() else {
return warnings.bail(Warning::invalid_type(elem), elem);
};
let pending_str = s.has_escapes(elem).gather_warnings_into(&mut warnings);
let s = match pending_str {
json::decode::PendingStr::NoEscapes(s) => s,
json::decode::PendingStr::HasEscapes(_) => {
return warnings.bail(Warning::ContainsEscapeCodes, elem);
}
};
if !s.chars().all(char::is_uppercase) {
warnings.insert(Warning::PreferUpperCase, elem);
}
let Some(index) = DAYS.iter().position(|day| day.eq_ignore_ascii_case(s)) else {
return warnings.bail(Warning::InvalidDay, elem);
};
let Ok(day) = Weekday::try_from(index) else {
return warnings.bail(Warning::InvalidDay, elem);
};
Ok(day.into_caveat(warnings))
}
}
impl From<Weekday> for chrono::Weekday {
fn from(day: Weekday) -> Self {
match day {
Weekday::Monday => Self::Mon,
Weekday::Tuesday => Self::Tue,
Weekday::Wednesday => Self::Wed,
Weekday::Thursday => Self::Thu,
Weekday::Friday => Self::Fri,
Weekday::Saturday => Self::Sat,
Weekday::Sunday => Self::Sun,
}
}
}