plex-api 0.0.12

Library for communication with Plex server. Work in progress, not ready for any use. See github for details.
Documentation
use serde::{
    de::{Error as DeError, SeqAccess, Visitor},
    Deserializer,
};
use serde_aux::prelude::{
    deserialize_bool_from_anything, deserialize_option_number_from_string,
    deserialize_string_from_number,
};
use serde_with::{formats::Separator, DeserializeAs};
use std::{
    fmt::{self, Display},
    marker::PhantomData,
    str::FromStr,
};
use time::OffsetDateTime;

pub(crate) fn optional_boolish<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
where
    D: Deserializer<'de>,
{
    Ok(Some(deserialize_bool_from_anything(deserializer)?))
}

pub(crate) fn deserialize_option_string_from_number<'de, D>(
    deserializer: D,
) -> Result<Option<String>, D::Error>
where
    D: Deserializer<'de>,
{
    Ok(Some(deserialize_string_from_number(deserializer)?))
}

pub(crate) fn deserialize_option_datetime_from_timestamp<'de, D>(
    deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error>
where
    D: Deserializer<'de>,
{
    deserialize_option_number_from_string(deserializer)?
        .map(OffsetDateTime::from_unix_timestamp)
        .transpose()
        .map_err(DeError::custom)
}

pub(crate) struct StringWithSeparatorOrList<Sep, T>(PhantomData<(Sep, T)>);

impl<'de, SEPARATOR, I, T> DeserializeAs<'de, I> for StringWithSeparatorOrList<SEPARATOR, T>
where
    SEPARATOR: Separator,
    I: FromIterator<T>,
    T: FromStr,
    T::Err: Display,
{
    fn deserialize_as<D>(deserializer: D) -> Result<I, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct Helper<SEPARATOR, I, T>(PhantomData<(SEPARATOR, I, T)>);

        impl<'de, SEPARATOR, I, T> Visitor<'de> for Helper<SEPARATOR, I, T>
        where
            SEPARATOR: Separator,
            I: FromIterator<T>,
            T: FromStr,
            T::Err: Display,
        {
            type Value = I;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(formatter, "a string")
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
            where
                E: DeError,
            {
                if value.is_empty() {
                    Ok(None.into_iter().collect())
                } else {
                    value
                        .split(SEPARATOR::separator())
                        .map(FromStr::from_str)
                        .collect::<Result<_, _>>()
                        .map_err(DeError::custom)
                }
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: SeqAccess<'de>,
            {
                let mut ret: Vec<T> = Vec::with_capacity(seq.size_hint().unwrap_or_default());

                while let Some(item) = seq.next_element::<&str>()? {
                    ret.push(FromStr::from_str(item).map_err(DeError::custom)?)
                }

                Ok(ret.into_iter().collect())
            }
        }

        deserializer.deserialize_any(Helper::<SEPARATOR, I, T>(PhantomData))
    }
}