tmdb_api/movie/
videos.rs

1use std::borrow::Cow;
2
3use crate::common::video::Video;
4
5/// Get a list of recommended movies for a movie.
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::videos::MovieVideos;
12///
13/// #[tokio::main]
14/// async fn main() {
15///     let client = Client::<ReqwestExecutor>::new("this-is-my-secret-token".into());
16///     let cmd = MovieVideos::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 MovieVideos {
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}
31
32impl MovieVideos {
33    pub fn new(movie_id: u64) -> Self {
34        Self {
35            movie_id,
36            language: None,
37        }
38    }
39
40    pub fn with_language(mut self, value: Option<String>) -> Self {
41        self.language = value;
42        self
43    }
44}
45
46#[derive(Debug, Deserialize)]
47pub struct MovieVideosResult {
48    pub id: u64,
49    pub results: Vec<Video>,
50}
51
52impl crate::prelude::Command for MovieVideos {
53    type Output = MovieVideosResult;
54
55    fn path(&self) -> Cow<'static, str> {
56        Cow::Owned(format!("/movie/{}/videos", self.movie_id))
57    }
58
59    fn params(&self) -> Vec<(&'static str, Cow<'_, str>)> {
60        let mut res = Vec::with_capacity(1);
61        if let Some(language) = self.language.as_ref() {
62            res.push(("language", Cow::Borrowed(language.as_str())));
63        }
64        res
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::MovieVideos;
71    use crate::client::Client;
72    use crate::client::reqwest::ReqwestExecutor;
73    use crate::prelude::Command;
74    use mockito::Matcher;
75
76    #[tokio::test]
77    async fn it_works() {
78        let mut server = mockito::Server::new_async().await;
79        let client = Client::<ReqwestExecutor>::builder()
80            .with_api_key("secret".into())
81            .with_base_url(server.url())
82            .build()
83            .unwrap();
84
85        let _m = server
86            .mock("GET", "/movie/550/videos")
87            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
88            .with_status(200)
89            .with_header("content-type", "application/json")
90            .with_body(include_str!("../../assets/movie-videos.json"))
91            .create_async()
92            .await;
93
94        let result = MovieVideos::new(550).execute(&client).await.unwrap();
95        assert_eq!(result.id, 550);
96        assert!(!result.results.is_empty());
97    }
98
99    #[tokio::test]
100    async fn invalid_api_key() {
101        let mut server = mockito::Server::new_async().await;
102        let client = Client::<ReqwestExecutor>::builder()
103            .with_api_key("secret".into())
104            .with_base_url(server.url())
105            .build()
106            .unwrap();
107
108        let _m = server
109            .mock("GET", "/movie/550/videos")
110            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
111            .with_status(401)
112            .with_header("content-type", "application/json")
113            .with_body(include_str!("../../assets/invalid-api-key.json"))
114            .create_async()
115            .await;
116
117        let err = MovieVideos::new(550).execute(&client).await.unwrap_err();
118        let server_err = err.as_server_error().unwrap();
119        assert_eq!(server_err.status_code, 7);
120    }
121
122    #[tokio::test]
123    async fn resource_not_found() {
124        let mut server = mockito::Server::new_async().await;
125        let client = Client::<ReqwestExecutor>::builder()
126            .with_api_key("secret".into())
127            .with_base_url(server.url())
128            .build()
129            .unwrap();
130
131        let _m = server
132            .mock("GET", "/movie/550/videos")
133            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
134            .with_status(404)
135            .with_header("content-type", "application/json")
136            .with_body(include_str!("../../assets/resource-not-found.json"))
137            .create_async()
138            .await;
139
140        let err = MovieVideos::new(550).execute(&client).await.unwrap_err();
141        let server_err = err.as_server_error().unwrap();
142        assert_eq!(server_err.status_code, 34);
143    }
144}
145
146#[cfg(all(test, feature = "integration"))]
147mod integration_tests {
148    use super::MovieVideos;
149    use crate::client::Client;
150    use crate::client::reqwest::ReqwestExecutor;
151    use crate::prelude::Command;
152
153    #[tokio::test]
154    async fn execute() {
155        let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
156        let client = Client::<ReqwestExecutor>::new(secret);
157
158        let result = MovieVideos::new(550).execute(&client).await.unwrap();
159        assert_eq!(result.id, 550);
160    }
161}