entertainarr-adapter-http 0.1.1

HTTP adapter for entertainarr
Documentation
use std::borrow::Cow;

pub mod auth;
pub mod kind;
pub mod language;
pub mod media_file;
pub mod podcast;
pub mod podcast_episode;
pub mod podcast_subscription;
pub mod podcast_task;
pub mod prelude;
pub mod task;
pub mod tvshow;
pub mod tvshow_episode;
pub mod tvshow_season;
pub mod tvshow_subscription;

fn default_includes<T>() -> Vec<T> {
    Vec::new()
}

pub trait IsVoid {
    fn is_void(&self) -> bool;
}

impl IsVoid for () {
    fn is_void(&self) -> bool {
        true
    }
}

impl<T> IsVoid for Vec<T> {
    fn is_void(&self) -> bool {
        self.is_empty()
    }
}

impl<T> IsVoid for Option<T> {
    fn is_void(&self) -> bool {
        self.is_none()
    }
}

#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Entity<Id, Kind> {
    pub id: Id,
    #[serde(rename = "type")]
    pub kind: Kind,
}

impl<Id, Kind: Default> Entity<Id, Kind> {
    pub fn new(id: Id) -> Self {
        Self {
            id,
            kind: Default::default(),
        }
    }
}

#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApiResource<T, Inc = ()> {
    pub data: T,
    #[serde(default = "default_includes", skip_serializing_if = "Vec::is_empty")]
    pub includes: Vec<Inc>,
}

impl<T> ApiResource<T> {
    pub fn new(data: T) -> Self {
        Self {
            data,
            includes: Vec::new(),
        }
    }
}

#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Relation<T, Meta = ()>
where
    Meta: IsVoid,
{
    pub data: Option<T>,
    #[serde(default, skip_serializing_if = "IsVoid::is_void")]
    pub meta: Meta,
}

#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[derive(Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ApiError {
    pub message: Cow<'static, str>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub detail: Option<ApiErrorDetail>,
}

#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[derive(Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ApiErrorDetail {
    pub attribute: Cow<'static, str>,
    pub code: Cow<'static, str>,
}

impl ApiErrorDetail {
    pub fn new(
        attribute: impl Into<Cow<'static, str>>,
        code: impl Into<Cow<'static, str>>,
    ) -> Self {
        Self {
            attribute: attribute.into(),
            code: code.into(),
        }
    }
}

#[cfg_attr(feature = "facet", derive(facet::Facet))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Couple(pub u64, pub u64);

impl serde::Serialize for Couple {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let value = format!("{}#{}", self.0, self.1);
        serializer.serialize_str(value.as_str())
    }
}

impl<'de> serde::de::Deserialize<'de> for Couple {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let value = String::deserialize(deserializer)?;
        let Some((left, right)) = value.split_once('#') else {
            return Err(serde::de::Error::custom("invalid format"));
        };
        let left = left.parse::<u64>().map_err(serde::de::Error::custom)?;
        let right = right.parse::<u64>().map_err(serde::de::Error::custom)?;
        Ok(Couple(left, right))
    }
}