rosu 0.6.0

An osu!api v1 wrapper
Documentation
use crate::model::GameMods;
use serde::{
    de::{Error, Unexpected, Visitor},
    Deserialize, Deserializer, Serialize, Serializer,
};
use std::{
    fmt::{Formatter, Result as FmtResult},
    str::FromStr,
};

struct ModsVisitor;

impl<'de> Visitor<'de> for ModsVisitor {
    type Value = Option<GameMods>;

    fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
        f.write_str("a u32, a stringified number, or null")
    }

    fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
        match u32::from_str(v) {
            Ok(n) => Ok(GameMods::from_bits(n)),
            Err(_) => GameMods::from_str(v)
                .map(Some)
                .map_err(|_| Error::invalid_value(Unexpected::Str(v), &"GameMods")),
        }
    }

    fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
        Ok(GameMods::from_bits(v as u32))
    }

    fn visit_some<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
        d.deserialize_any(Self)
    }

    #[inline]
    fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
        Ok(None)
    }
}

pub(crate) fn to_maybe_mods<'de, D: Deserializer<'de>>(d: D) -> Result<Option<GameMods>, D::Error> {
    d.deserialize_option(ModsVisitor)
}

impl<'de> Deserialize<'de> for GameMods {
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        Ok(d.deserialize_any(ModsVisitor)?.unwrap_or_default())
    }
}

impl Serialize for GameMods {
    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        s.serialize_u32(self.bits())
    }
}