TheMovieDB 0.1.0

A robust and idiomatic Rust API wrapper for The Movie Database (TMDb) v3 API.
Documentation
use crate::base::TMDB;
use std::collections::HashMap;
use serde_json::Value;

macro_rules! tmdb_struct {
    ($name:ident, $base_path:literal, { $($url_key:literal => $url_val:literal),* $(,)? }) => {
        pub struct $name {
            tmdb: TMDB,
            id: i32,
        }

        impl $name {
            pub fn new(id: i32) -> Self {
                let mut tmdb = TMDB::new();
                tmdb.base_path = $base_path.to_string();
                tmdb.urls = [
                    $(($url_key.to_string(), $url_val.to_string())),*
                ]
                .iter()
                .cloned()
                .collect();
                $name { tmdb, id }
            }

            pub fn _get_id_path(&self, key: &str) -> String {
                self.tmdb._get_path(key).replace("{id}", &self.id.to_string())
            }
        }
    };
}

macro_rules! tmdb_method {
    ($struct_name:ident, $method_name:ident, $url_key:literal) => {
        impl $struct_name {
            pub async fn $method_name(&self, options: Option<HashMap<String, String>>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
                let path = self._get_id_path($url_key);
                self.tmdb._get(&path, options).await
            }
        }
    };
    ($struct_name:ident, $method_name:ident, $url_key:literal, POST) => {
        impl $struct_name {
            pub async fn $method_name(&self, options: Option<HashMap<String, String>>, payload: Option<Value>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
                let path = self._get_id_path($url_key);
                self.tmdb._post(&path, options, payload).await
            }
        }
    };
    ($struct_name:ident, $method_name:ident, $url_key:literal, DELETE) => {
        impl $struct_name {
            pub async fn $method_name(&self, options: Option<HashMap<String, String>>, payload: Option<Value>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
                let path = self._get_id_path($url_key);
                self.tmdb._delete(&path, options, payload).await
            }
        }
    };
}

tmdb_struct!(Movies, "movie", {
    "info" => "/{id}",
    "account_states" => "/{id}/account_states",
    "alternative_titles" => "/{id}/alternative_titles",
    "changes" => "/{id}/changes",
    "credits" => "/{id}/credits",
    "external_ids" => "/{id}/external_ids",
    "images" => "/{id}/images",
    "keywords" => "/{id}/keywords",
    "lists" => "/{id}/lists",
    "recommendations" => "/{id}/recommendations",
    "release_dates" => "/{id}/release_dates",
    "reviews" => "/{id}/reviews",
    "similar_movies" => "/{id}/similar_movies",
    "translations" => "/{id}/translations",
    "videos" => "/{id}/videos",
    "watch_providers" => "/{id}/watch/providers",
    "rating" => "/{id}/rating",
    "rating_delete" => "/{id}/rating",
    "latest" => "/latest",
    "now_playing" => "/now_playing",
    "popular" => "/popular",
    "top_rated" => "/top_rated",
    "upcoming" => "/upcoming",
    "releases" => "/{id}/releases",
});

tmdb_method!(Movies, info, "info");
tmdb_method!(Movies, account_states, "account_states");
tmdb_method!(Movies, alternative_titles, "alternative_titles");
tmdb_method!(Movies, changes, "changes");
tmdb_method!(Movies, credits, "credits");
tmdb_method!(Movies, external_ids, "external_ids");
tmdb_method!(Movies, images, "images");
tmdb_method!(Movies, keywords, "keywords");
tmdb_method!(Movies, lists, "lists");
tmdb_method!(Movies, recommendations, "recommendations");
tmdb_method!(Movies, release_dates, "release_dates");
tmdb_method!(Movies, reviews, "reviews");
tmdb_method!(Movies, similar_movies, "similar_movies");
tmdb_method!(Movies, translations, "translations");
tmdb_method!(Movies, videos, "videos");
tmdb_method!(Movies, watch_providers, "watch_providers");
tmdb_method!(Movies, rating, "rating", POST);
tmdb_method!(Movies, rating_delete, "rating_delete", DELETE);
tmdb_method!(Movies, releases, "releases");

impl Movies {
    pub async fn latest(&self, options: Option<HashMap<String, String>>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
        let path = self.tmdb._get_path("latest");
        self.tmdb._get(&path, options).await
    }

    pub async fn now_playing(&self, options: Option<HashMap<String, String>>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
        let path = self.tmdb._get_path("now_playing");
        self.tmdb._get(&path, options).await
    }

    pub async fn popular(&self, options: Option<HashMap<String, String>>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
        let path = self.tmdb._get_path("popular");
        self.tmdb._get(&path, options).await
    }

    pub async fn top_rated(&self, options: Option<HashMap<String, String>>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
        let path = self.tmdb._get_path("top_rated");
        self.tmdb._get(&path, options).await
    }

    pub async fn upcoming(&self, options: Option<HashMap<String, String>>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
        let path = self.tmdb._get_path("upcoming");
        self.tmdb._get(&path, options).await
    }
}

tmdb_struct!(Collections, "collection", {
    "info" => "/{id}",
    "images" => "/{id}/images",
    "translations" => "/{id}/translations",
});

tmdb_method!(Collections, info, "info");
tmdb_method!(Collections, images, "images");
tmdb_method!(Collections, translations, "translations");

tmdb_struct!(Companies, "company", {
    "info" => "/{id}",
    "alternative_names" => "/{id}/alternative_names",
    "images" => "/{id}/images",
    "movies" => "/{id}/movies",
});

tmdb_method!(Companies, info, "info");
tmdb_method!(Companies, alternative_names, "alternative_names");
tmdb_method!(Companies, images, "images");
tmdb_method!(Companies, movies, "movies");

tmdb_struct!(Keywords, "keyword", {
    "info" => "/{id}",
    "movies" => "/{id}/movies",
});

tmdb_method!(Keywords, info, "info");
tmdb_method!(Keywords, movies, "movies");

pub struct Reviews {
    tmdb: TMDB,
    id: String,
}

impl Reviews {
    pub fn new(id: String) -> Self {
        let mut tmdb = TMDB::new();
        tmdb.base_path = "review".to_string();
        tmdb.urls = [("info".to_string(), "/{id}".to_string())]
            .iter()
            .cloned()
            .collect();
        Reviews { tmdb, id }
    }

    pub fn _get_id_path(&self, key: &str) -> String {
        self.tmdb._get_path(key).replace("{id}", &self.id)
    }

    pub async fn info(&self, options: Option<HashMap<String, String>>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
        let path = self._get_id_path("info");
        self.tmdb._get(&path, options).await
    }
}