tmdb_api/movie/
lists.rs

1use std::borrow::Cow;
2
3use crate::common::PaginatedResult;
4
5/// Get a list of lists that this movie belongs to.
6///
7/// ```rust
8/// use tmdb_api::prelude::Command;
9/// use tmdb_api::client::Client;
10/// use tmdb_api::client::reqwest::ReqwestExecutor;
11/// use tmdb_api::movie::lists::MovieLists;
12///
13/// #[tokio::main]
14/// async fn main() {
15///     let client = Client::<ReqwestExecutor>::new("this-is-my-secret-token".into());
16///     let cmd = MovieLists::new(1);
17///     let result = cmd.execute(&client).await;
18///     match result {
19///         Ok(res) => println!("found: {:#?}", res),
20///         Err(err) => eprintln!("error: {:?}", err),
21///     };
22/// }
23/// ```
24#[derive(Clone, Debug, Default)]
25pub struct MovieLists {
26    /// ID of the movie.
27    pub movie_id: u64,
28    /// ISO 639-1 value to display translated data for the fields that support it.
29    pub language: Option<String>,
30    /// Specify which page to query.
31    pub page: Option<u32>,
32}
33
34impl MovieLists {
35    pub fn new(movie_id: u64) -> Self {
36        Self {
37            movie_id,
38            language: None,
39            page: None,
40        }
41    }
42
43    pub fn with_language(mut self, value: Option<String>) -> Self {
44        self.language = value;
45        self
46    }
47
48    pub fn with_page(mut self, value: Option<u32>) -> Self {
49        self.page = value;
50        self
51    }
52}
53
54#[derive(Debug, Deserialize, Serialize)]
55pub struct MovieList {
56    pub id: u64,
57    pub name: String,
58    #[serde(deserialize_with = "crate::util::empty_string::deserialize")]
59    pub description: Option<String>,
60    pub list_type: String,
61    pub poster_path: Option<String>,
62    pub iso_639_1: String,
63    pub item_count: u64,
64    pub favorite_count: u64,
65}
66
67impl crate::prelude::Command for MovieLists {
68    type Output = PaginatedResult<MovieList>;
69
70    fn path(&self) -> Cow<'static, str> {
71        Cow::Owned(format!("/movie/{}/lists", self.movie_id))
72    }
73
74    fn params(&self) -> Vec<(&'static str, Cow<'_, str>)> {
75        let mut res = Vec::with_capacity(2);
76        if let Some(language) = self.language.as_ref() {
77            res.push(("language", Cow::Borrowed(language.as_str())));
78        }
79        if let Some(page) = self.page {
80            res.push(("page", Cow::Owned(page.to_string())));
81        }
82        res
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use mockito::Matcher;
89
90    use crate::client::Client;
91    use crate::client::reqwest::ReqwestExecutor;
92    use crate::prelude::Command;
93
94    use super::MovieLists;
95
96    #[tokio::test]
97    async fn it_works() {
98        let mut server = mockito::Server::new_async().await;
99        let client = Client::<ReqwestExecutor>::builder()
100            .with_api_key("secret".into())
101            .with_base_url(server.url())
102            .build()
103            .unwrap();
104
105        let _m = server
106            .mock("GET", "/movie/550/lists")
107            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
108            .with_status(200)
109            .with_header("content-type", "application/json")
110            .with_body(include_str!("../../assets/movie-lists.json"))
111            .create_async()
112            .await;
113
114        let result = MovieLists::new(550).execute(&client).await.unwrap();
115        assert_eq!(result.page, 1);
116        assert_eq!(result.results.len(), 20);
117    }
118
119    #[tokio::test]
120    async fn invalid_api_key() {
121        let mut server = mockito::Server::new_async().await;
122        let client = Client::<ReqwestExecutor>::builder()
123            .with_api_key("secret".into())
124            .with_base_url(server.url())
125            .build()
126            .unwrap();
127
128        let _m = server
129            .mock("GET", "/movie/550/lists")
130            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
131            .with_status(401)
132            .with_header("content-type", "application/json")
133            .with_body(include_str!("../../assets/invalid-api-key.json"))
134            .create_async()
135            .await;
136
137        let err = MovieLists::new(550).execute(&client).await.unwrap_err();
138        let server_err = err.as_server_error().unwrap();
139        assert_eq!(server_err.status_code, 7);
140    }
141
142    #[tokio::test]
143    async fn resource_not_found() {
144        let mut server = mockito::Server::new_async().await;
145        let client = Client::<ReqwestExecutor>::builder()
146            .with_api_key("secret".into())
147            .with_base_url(server.url())
148            .build()
149            .unwrap();
150
151        let _m = server
152            .mock("GET", "/movie/550/lists")
153            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
154            .with_status(404)
155            .with_header("content-type", "application/json")
156            .with_body(include_str!("../../assets/resource-not-found.json"))
157            .create_async()
158            .await;
159
160        let err = MovieLists::new(550).execute(&client).await.unwrap_err();
161        let server_err = err.as_server_error().unwrap();
162        assert_eq!(server_err.status_code, 34);
163    }
164}
165
166#[cfg(all(test, feature = "integration"))]
167mod integration_tests {
168    use crate::client::Client;
169    use crate::client::reqwest::ReqwestExecutor;
170    use crate::prelude::Command;
171
172    use super::MovieLists;
173
174    #[tokio::test]
175    async fn execute() {
176        let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
177        let client = Client::<ReqwestExecutor>::new(secret);
178
179        let result = MovieLists::new(550).execute(&client).await.unwrap();
180        assert_eq!(result.page, 1);
181    }
182}