Documentation
use core::{convert::TryFrom as _, fmt};

use serde::{
    de::{self, Visitor},
    Deserialize, Deserializer, Serialize, Serializer,
};

use crate::Weekday;

pub mod serde_str {
    use serde::{Deserializer, Serializer};

    use super::{Weekday, WeekdayVisitor};

    pub fn serialize<S>(weekday: &Weekday, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(weekday.to_string().as_str())
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Weekday, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(WeekdayVisitor)
    }
}

pub mod serde_u64 {
    use serde::{Deserializer, Serializer};

    use super::{Weekday, WeekdayVisitor};

    pub fn serialize<S>(weekday: &Weekday, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_u64(weekday.to_owned() as u64)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Weekday, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_u64(WeekdayVisitor)
    }
}

impl Serialize for Weekday {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.to_string().as_str())
    }
}
impl<'de> Deserialize<'de> for Weekday {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_any(WeekdayVisitor)
    }
}

struct WeekdayVisitor;
impl<'de> Visitor<'de> for WeekdayVisitor {
    type Value = Weekday;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("str or u8")
    }

    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        v.parse::<Self::Value>().map_err(de::Error::custom)
    }

    fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Self::Value::try_from(v as u8).map_err(de::Error::custom)
    }

    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Self::Value::try_from(v as u8).map_err(de::Error::custom)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use serde_json::json;

    #[derive(Deserialize, Serialize, PartialEq, Debug)]
    struct Foo {
        #[serde(with = "crate::serde_str")]
        w1: Weekday,
        #[serde(with = "crate::serde_u64")]
        w2: Weekday,
        #[serde(
            serialize_with = "crate::serde_str::serialize",
            deserialize_with = "crate::serde_u64::deserialize"
        )]
        w3: Weekday,
        w4: Weekday,
    }

    #[test]
    fn de() {
        let json = r#"{ "w1": "Monday", "w2": 2, "w3": 3, "w4": "Thu" }"#;
        assert_eq!(
            serde_json::from_str::<Foo>(json).unwrap(),
            Foo {
                w1: Weekday::Mon,
                w2: Weekday::Tue,
                w3: Weekday::Wed,
                w4: Weekday::Thu,
            }
        );

        let json = r#"{ "w1": "Monday", "w2": 2, "w3": 3, "w4": 4 }"#;
        assert_eq!(
            serde_json::from_str::<Foo>(json).unwrap(),
            Foo {
                w1: Weekday::Mon,
                w2: Weekday::Tue,
                w3: Weekday::Wed,
                w4: Weekday::Thu,
            }
        );
    }

    #[test]
    fn ser() {
        assert_eq!(
            serde_json::to_value(&Foo {
                w1: Weekday::Mon,
                w2: Weekday::Tue,
                w3: Weekday::Wed,
                w4: Weekday::Thu,
            })
            .unwrap(),
            json!({
                "w1": "Mon", "w2": 2, "w3": "Wed", "w4": "Thu"
            })
        );
    }
}