entertainarr-adapter-http 0.1.1

HTTP adapter for entertainarr
Documentation
use std::collections::HashSet;

use axum::routing::get;
use entertainarr_domain::tvshow::entity::{
    FindTvShowResponse, ListTvShowResponse, TvShowWithSubscription,
};

use crate::entity::tvshow::{
    TvShowAttributes, TvShowDocument, TvShowInclude, TvShowRelation, TvShowRelationships,
    TvShowSource,
};
use crate::entity::tvshow_subscription::{
    TvShowSubscriptionAttributes, TvShowSubscriptionDocument, TvShowSubscriptionEntity,
};
use crate::entity::{ApiResource, Couple, Relation};
use crate::server::handler::prelude::FromDomainResponse;

mod find;
mod subscription;
mod user_list;

pub fn create<S>() -> axum::Router<S>
where
    S: crate::server::prelude::ServerState + Clone,
{
    axum::Router::new()
        .route("/tvshows/{tvshow_id}", get(find::handle::<S>))
        .route(
            "/users/me/tvshows",
            get(user_list::handle::<S>).post(subscription::create::handle::<S>),
        )
}

impl From<entertainarr_domain::tvshow::entity::TvShowSource> for TvShowSource {
    fn from(value: entertainarr_domain::tvshow::entity::TvShowSource) -> Self {
        match value {
            entertainarr_domain::tvshow::entity::TvShowSource::Tmdb { tmdb_id } => {
                TvShowSource::Tmdb { tmdb_id }
            }
        }
    }
}

impl From<TvShowSource> for entertainarr_domain::tvshow::entity::TvShowSource {
    fn from(value: TvShowSource) -> Self {
        match value {
            TvShowSource::Tmdb { tmdb_id } => {
                entertainarr_domain::tvshow::entity::TvShowSource::Tmdb { tmdb_id }
            }
        }
    }
}

impl From<entertainarr_domain::tvshow::entity::TvShow> for TvShowAttributes {
    fn from(tvshow: entertainarr_domain::tvshow::entity::TvShow) -> Self {
        Self {
            source: tvshow.source.into(),
            original_name: tvshow.original_name,
            original_language: tvshow.original_language,
            origin_country: tvshow.origin_country,
            language: tvshow.language.into(),
            name: tvshow.name,
            overview: tvshow.overview,
            tagline: tvshow.tagline,
            poster_url: tvshow.poster_url,
            backdrop_url: tvshow.backdrop_url,
            homepage: tvshow.homepage,
            first_air_date: tvshow.first_air_date,
            in_production: tvshow.in_production,
            adult: tvshow.adult,
            created_at: tvshow.created_at,
            updated_at: tvshow.updated_at,
        }
    }
}

impl From<entertainarr_domain::tvshow::entity::TvShowWithSubscription> for TvShowDocument {
    fn from(value: entertainarr_domain::tvshow::entity::TvShowWithSubscription) -> Self {
        let entertainarr_domain::tvshow::entity::TvShowWithSubscription {
            tvshow,
            subscription: _,
        } = value;
        Self {
            id: tvshow.id,
            kind: Default::default(),
            relationships: TvShowRelationships {
                subscription: Relation {
                    data: Some(TvShowSubscriptionEntity {
                        id: Couple(tvshow.id, 0),
                        kind: Default::default(),
                    }),
                    meta: (),
                },
            },
            attributes: tvshow.into(),
        }
    }
}

impl ApiResource<Vec<TvShowDocument>, TvShowRelation> {
    pub fn from_tvshow_subscription_list(list: Vec<TvShowWithSubscription>) -> Self {
        let mut data = Vec::with_capacity(list.len());
        list.into_iter().for_each(|item| {
            data.push(TvShowDocument::from(item));
        });
        Self {
            data,
            includes: Vec::new(),
        }
    }
}

impl FromDomainResponse<FindTvShowResponse, TvShowInclude>
    for ApiResource<TvShowDocument, TvShowRelation>
{
    fn from_response(response: FindTvShowResponse, required: HashSet<TvShowInclude>) -> Self {
        let data = TvShowDocument {
            id: response.tvshow.id,
            kind: Default::default(),
            attributes: TvShowAttributes::from(response.tvshow),
            relationships: TvShowRelationships {
                subscription: Relation {
                    data: response.subscription.as_ref().map(|sub| {
                        TvShowSubscriptionEntity::new(Couple(sub.tvshow_id, sub.user_id))
                    }),
                    meta: (),
                },
            },
        };

        let mut includes = Vec::with_capacity(1);

        if required.contains(&TvShowInclude::Subscription)
            && let Some(sub) = response.subscription
        {
            includes.push(TvShowRelation::TvShowSubscription(
                TvShowSubscriptionDocument {
                    id: Couple(sub.tvshow_id, sub.user_id),
                    kind: Default::default(),
                    attributes: TvShowSubscriptionAttributes {
                        created_at: sub.created_at,
                    },
                },
            ));
        }
        ApiResource { data, includes }
    }
}

impl FromDomainResponse<ListTvShowResponse, TvShowInclude>
    for ApiResource<Vec<TvShowDocument>, TvShowRelation>
{
    fn from_response(response: ListTvShowResponse, required: HashSet<TvShowInclude>) -> Self {
        let data = response
            .tvshows
            .into_iter()
            .map(|tvshow| TvShowDocument {
                id: tvshow.id,
                kind: Default::default(),
                relationships: TvShowRelationships {
                    subscription: Relation {
                        data: response.subscriptions.get(&tvshow.id).map(|sub| {
                            TvShowSubscriptionEntity::new(Couple(sub.tvshow_id, sub.user_id))
                        }),
                        meta: (),
                    },
                },
                attributes: tvshow.into(),
            })
            .collect();
        let mut includes = Vec::default();
        if required.contains(&TvShowInclude::Subscription) {
            includes.extend(
                response
                    .subscriptions
                    .into_values()
                    .map(|sub| TvShowRelation::TvShowSubscription(sub.into())),
            );
        }
        ApiResource { data, includes }
    }
}

impl From<entertainarr_domain::tvshow::entity::TvShowSubscription> for TvShowSubscriptionDocument {
    fn from(value: entertainarr_domain::tvshow::entity::TvShowSubscription) -> Self {
        TvShowSubscriptionDocument {
            id: Couple(value.tvshow_id, value.user_id),
            kind: Default::default(),
            attributes: TvShowSubscriptionAttributes {
                created_at: value.created_at,
            },
        }
    }
}