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,
        }

        impl $name {
            pub fn new() -> 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 }
            }
        }
    };
}

macro_rules! tmdb_method_get {
    ($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.tmdb._get_path($url_key);
                self.tmdb._get(&path, options).await
            }
        }
    };
}

macro_rules! tmdb_method_delete {
    ($struct_name:ident, $method_name:ident, $url_key:literal) => {
        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.tmdb._get_path($url_key);
                self.tmdb._delete(&path, options, payload).await
            }
        }
    };
}

pub struct Account {
    tmdb: TMDB,
    session_id: String,
    id: i32,
}

impl Account {
    pub fn new(session_id: String) -> Self {
        let mut tmdb = TMDB::new();
        tmdb.base_path = "account".to_string();
        tmdb.urls = [
            ("info".to_string(), "".to_string()),
            ("lists".to_string(), "/{id}/lists".to_string()),
            ("favorite_movies".to_string(), "/{id}/favorite/movies".to_string()),
            ("favorite_tv".to_string(), "/{id}/favorite/tv".to_string()),
            ("favorite".to_string(), "/{id}/favorite".to_string()),
            ("rated_movies".to_string(), "/{id}/rated/movies".to_string()),
            ("rated_tv".to_string(), "/{id}/rated/tv".to_string()),
            ("rated_tv_episodes".to_string(), "/{id}/rated/tv/episodes".to_string()),
            ("watchlist_movies".to_string(), "/{id}/watchlist/movies".to_string()),
            ("watchlist_tv".to_string(), "/{id}/watchlist/tv".to_string()),
            ("watchlist".to_string(), "/{id}/watchlist".to_string()),
        ]
        .iter()
        .cloned()
        .collect();
        Account { tmdb, session_id, id: 0 }
    }

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

    pub async fn info(&mut self, options: Option<HashMap<String, String>>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
        let path = self.tmdb._get_path("info");
        let mut params = options.unwrap_or_default();
        params.insert("session_id".to_string(), self.session_id.clone());
        let response = self.tmdb._get(&path, Some(params)).await?;
        if let Some(id) = response.get("id").and_then(|v| v.as_i64()) {
            self.id = id as i32;
        }
        Ok(response)
    }

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

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

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

    pub async fn favorite(&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("favorite");
        let mut params = options.unwrap_or_default();
        params.insert("session_id".to_string(), self.session_id.clone());
        self.tmdb._post(&path, Some(params), payload).await
    }

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

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

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

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

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

    pub async fn watchlist(&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("watchlist");
        let mut params = options.unwrap_or_default();
        params.insert("session_id".to_string(), self.session_id.clone());
        self.tmdb._post(&path, Some(params), payload).await
    }
}

tmdb_struct!(Authentication, "authentication", {
    "guest_session_new" => "/guest_session/new",
    "token_new" => "/token/new",
    "session_new" => "/session/new",
    "token_validate_with_login" => "/token/validate_with_login",
    "session_delete" => "/session",
});

tmdb_method_get!(Authentication, guest_session_new, "guest_session_new");
tmdb_method_get!(Authentication, token_new, "token_new");
tmdb_method_get!(Authentication, session_new, "session_new");
tmdb_method_get!(Authentication, token_validate_with_login, "token_validate_with_login");
tmdb_method_delete!(Authentication, session_delete, "session_delete");

pub struct GuestSessions {
    tmdb: TMDB,
    guest_session_id: String,
}

impl GuestSessions {
    pub fn new(guest_session_id: String) -> Self {
        let mut tmdb = TMDB::new();
        tmdb.base_path = "guest_session".to_string();
        tmdb.urls = [
            ("rated_movies".to_string(), "/{guest_session_id}/rated/movies".to_string()),
            ("rated_tv".to_string(), "/{guest_session_id}/rated/tv".to_string()),
            ("rated_tv_episodes".to_string(), "/{guest_session_id}/rated/tv/episodes".to_string()),
        ]
        .iter()
        .cloned()
        .collect();
        GuestSessions { tmdb, guest_session_id }
    }

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

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

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

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

pub struct Lists {
    tmdb: TMDB,
    id: i32,
    session_id: String,
}

impl Lists {
    pub fn new(id: i32, session_id: String) -> Self {
        let mut tmdb = TMDB::new();
        tmdb.base_path = "list".to_string();
        tmdb.urls = [
            ("info".to_string(), "/{id}".to_string()),
            ("item_status".to_string(), "/{id}/item_status".to_string()),
            ("list_create".to_string(), "".to_string()),
            ("add_item".to_string(), "/{id}/add_item".to_string()),
            ("remove_item".to_string(), "/{id}/remove_item".to_string()),
            ("list_clear".to_string(), "/{id}/clear".to_string()),
            ("list_delete".to_string(), "/{id}".to_string()),
        ]
        .iter()
        .cloned()
        .collect();
        Lists { tmdb, id, session_id }
    }

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

    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
    }

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

    pub async fn list_create(&mut self, options: Option<HashMap<String, String>>, payload: Option<Value>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
        let path = self.tmdb._get_path("list_create");
        let mut params = options.unwrap_or_default();
        params.insert("session_id".to_string(), self.session_id.clone());
        let response = self.tmdb._post(&path, Some(params), payload).await?;
        if let Some(id) = response.get("list_id").and_then(|v| v.as_i64()) {
            self.id = id as i32;
        }
        Ok(response)
    }

    pub async fn add_item(&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("add_item");
        let mut params = options.unwrap_or_default();
        params.insert("session_id".to_string(), self.session_id.clone());
        self.tmdb._post(&path, Some(params), payload).await
    }

    pub async fn remove_item(&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("remove_item");
        let mut params = options.unwrap_or_default();
        params.insert("session_id".to_string(), self.session_id.clone());
        self.tmdb._post(&path, Some(params), payload).await
    }

    pub async fn list_clear(&self, options: Option<HashMap<String, String>>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
        let path = self._get_id_path("list_clear");
        let mut params = options.unwrap_or_default();
        params.insert("session_id".to_string(), self.session_id.clone());
        self.tmdb._post(&path, Some(params), None).await
    }

    pub async fn list_delete(&self, options: Option<HashMap<String, String>>) -> Result<Value, Box<dyn std::error::Error + Send + Sync + 'static>> {
        let path = self._get_id_path("list_delete");
        let mut params = options.unwrap_or_default();
        params.insert("session_id".to_string(), self.session_id.clone());
        self.tmdb._delete(&path, Some(params), None).await
    }
}