tmdb_api/movie/
alternative_titles.rs

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