tmdb_api/tvshow/episode/
details.rs

1use std::borrow::Cow;
2
3/// Command to get the details of a tvshow episode
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::tvshow::episode::details::TVShowEpisodeDetails;
10///
11/// #[tokio::main]
12/// async fn main() {
13///     let client = Client::<ReqwestExecutor>::new("this-is-my-secret-token".into());
14///     let cmd = TVShowEpisodeDetails::new(1, 1, 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 TVShowEpisodeDetails {
24    /// ID of the TV Show
25    pub tv_id: u64,
26    /// Number of the season
27    pub season_number: u64,
28    /// Number of the episode
29    pub episode_number: u64,
30    /// ISO 639-1 value to display translated data for the fields that support it.
31    pub language: Option<String>,
32}
33
34impl TVShowEpisodeDetails {
35    pub fn new(tv_id: u64, season_number: u64, episode_number: u64) -> Self {
36        Self {
37            tv_id,
38            season_number,
39            episode_number,
40            language: None,
41        }
42    }
43
44    pub fn with_language(mut self, value: Option<String>) -> Self {
45        self.language = value;
46        self
47    }
48}
49
50impl crate::prelude::Command for TVShowEpisodeDetails {
51    type Output = crate::tvshow::Episode;
52
53    fn path(&self) -> Cow<'static, str> {
54        Cow::Owned(format!(
55            "/tv/{}/season/{}/episode/{}",
56            self.tv_id, self.season_number, self.episode_number
57        ))
58    }
59
60    fn params(&self) -> Vec<(&'static str, Cow<'_, str>)> {
61        if let Some(language) = self.language.as_ref() {
62            vec![("language", Cow::Borrowed(language.as_str()))]
63        } else {
64            Vec::new()
65        }
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::TVShowEpisodeDetails;
72    use crate::client::Client;
73    use crate::client::reqwest::ReqwestExecutor;
74    use crate::prelude::Command;
75    use mockito::Matcher;
76
77    #[tokio::test]
78    async fn it_works() {
79        let mut server = mockito::Server::new_async().await;
80        let client = Client::<ReqwestExecutor>::builder()
81            .with_api_key("secret".into())
82            .with_base_url(server.url())
83            .build()
84            .unwrap();
85
86        let _m = server
87            .mock("GET", "/tv/1399/season/1/episode/1")
88            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
89            .with_status(200)
90            .with_header("content-type", "application/json")
91            .with_body(include_str!("../../../assets/tv-episode-details.json"))
92            .create_async()
93            .await;
94
95        let result = TVShowEpisodeDetails::new(1399, 1, 1)
96            .execute(&client)
97            .await
98            .unwrap();
99        assert_eq!(result.inner.id, 63056);
100    }
101
102    #[tokio::test]
103    async fn invalid_api_key() {
104        let mut server = mockito::Server::new_async().await;
105        let client = Client::<ReqwestExecutor>::builder()
106            .with_api_key("secret".into())
107            .with_base_url(server.url())
108            .build()
109            .unwrap();
110
111        let _m = server
112            .mock("GET", "/tv/1399/season/1/episode/1")
113            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
114            .with_status(401)
115            .with_header("content-type", "application/json")
116            .with_body(include_str!("../../../assets/invalid-api-key.json"))
117            .create_async()
118            .await;
119
120        let err = TVShowEpisodeDetails::new(1399, 1, 1)
121            .execute(&client)
122            .await
123            .unwrap_err();
124        let server_err = err.as_server_error().unwrap();
125        assert_eq!(server_err.status_code, 7);
126    }
127
128    #[tokio::test]
129    async fn resource_not_found() {
130        let mut server = mockito::Server::new_async().await;
131        let client = Client::<ReqwestExecutor>::builder()
132            .with_api_key("secret".into())
133            .with_base_url(server.url())
134            .build()
135            .unwrap();
136
137        let _m = server
138            .mock("GET", "/tv/1399/season/1/episode/1")
139            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
140            .with_status(404)
141            .with_header("content-type", "application/json")
142            .with_body(include_str!("../../../assets/resource-not-found.json"))
143            .create_async()
144            .await;
145
146        let err = TVShowEpisodeDetails::new(1399, 1, 1)
147            .execute(&client)
148            .await
149            .unwrap_err();
150        let server_err = err.as_server_error().unwrap();
151        assert_eq!(server_err.status_code, 34);
152    }
153}
154
155#[cfg(all(test, feature = "integration"))]
156mod integration_tests {
157    use super::TVShowEpisodeDetails;
158    use crate::client::Client;
159    use crate::client::reqwest::ReqwestExecutor;
160    use crate::prelude::Command;
161
162    #[tokio::test]
163    async fn execute() {
164        let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
165        let client = Client::<ReqwestExecutor>::new(secret);
166
167        for (tv_id, season_id) in [(1, 1130462u64)] {
168            let result = TVShowEpisodeDetails::new(tv_id, 1, 1)
169                .execute(&client)
170                .await
171                .unwrap();
172            assert_eq!(result.inner.id, season_id);
173        }
174    }
175}