tmdb_api/tvshow/
aggregate_credits.rs

1//! https://developer.themoviedb.org/reference/tv-series-aggregate-credits
2
3use std::borrow::Cow;
4
5/// Command to get the aggregate credits of a TV show.
6///
7/// ```rust
8/// use tmdb_api::prelude::Command;
9/// use tmdb_api::Client;
10/// use tmdb_api::client::reqwest::ReqwestExecutor;
11/// use tmdb_api::tvshow::aggregate_credits::TVShowAggregateCredits;
12///
13/// #[tokio::main]
14/// async fn main() {
15///     let client = Client::<ReqwestExecutor>::new("this-is-my-secret-token".into());
16///     let cmd = TVShowAggregateCredits::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 TVShowAggregateCredits {
26    pub id: u64,
27    pub language: Option<String>,
28}
29
30#[derive(Debug, Deserialize)]
31pub struct TVShowAggregateCreditsResult {
32    pub id: u64,
33    pub cast: Vec<CastPerson>,
34    pub crew: Vec<CrewPerson>,
35}
36
37#[derive(Debug, Deserialize)]
38pub struct CastPerson {
39    #[serde(flatten)]
40    pub inner: Person,
41    pub roles: Vec<Role>,
42    pub order: u64,
43}
44
45#[derive(Debug, Deserialize)]
46pub struct CrewPerson {
47    #[serde(flatten)]
48    pub inner: Person,
49    pub jobs: Vec<Job>,
50    pub department: String,
51}
52
53#[derive(Debug, Deserialize)]
54pub struct Person {
55    pub id: u64,
56    pub adult: bool,
57    pub gender: u64,
58    pub known_for_department: String,
59    pub name: String,
60    pub original_name: String,
61    pub popularity: f64,
62    pub profile_path: Option<String>,
63    pub total_episode_count: u64,
64}
65
66#[derive(Debug, Deserialize)]
67pub struct Role {
68    pub credit_id: String,
69    pub character: String,
70    pub episode_count: u64,
71}
72
73#[derive(Debug, Deserialize)]
74pub struct Job {
75    pub credit_id: String,
76    pub job: String,
77    pub episode_count: u64,
78}
79
80impl TVShowAggregateCredits {
81    pub fn new(tv_show_id: u64) -> Self {
82        Self {
83            id: tv_show_id,
84            language: None,
85        }
86    }
87
88    pub fn with_language(mut self, value: Option<String>) -> Self {
89        self.language = value;
90        self
91    }
92}
93
94impl crate::prelude::Command for TVShowAggregateCredits {
95    type Output = TVShowAggregateCreditsResult;
96
97    fn path(&self) -> Cow<'static, str> {
98        Cow::Owned(format!("/tv/{}/aggregate_credits", self.id))
99    }
100
101    fn params(&self) -> Vec<(&'static str, Cow<'_, str>)> {
102        if let Some(ref language) = self.language {
103            vec![("language", Cow::Borrowed(language))]
104        } else {
105            Vec::new()
106        }
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use mockito::Matcher;
113
114    use crate::Client;
115    use crate::client::reqwest::ReqwestExecutor;
116    use crate::prelude::Command;
117
118    use super::TVShowAggregateCredits;
119
120    #[tokio::test]
121    async fn it_works() {
122        let mut server = mockito::Server::new_async().await;
123        let client = Client::<ReqwestExecutor>::builder()
124            .with_api_key("secret".into())
125            .with_base_url(server.url())
126            .build()
127            .unwrap();
128
129        let _m = server
130            .mock("GET", "/tv/1399/aggregate_credits")
131            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
132            .with_status(200)
133            .with_header("content-type", "application/json")
134            .with_body(include_str!("../../assets/tv-aggregate-credits.json"))
135            .create_async()
136            .await;
137
138        let result = TVShowAggregateCredits::new(1399)
139            .execute(&client)
140            .await
141            .unwrap();
142        assert_eq!(result.id, 1399);
143        assert!(!result.cast.is_empty());
144        assert!(!result.crew.is_empty());
145    }
146
147    #[tokio::test]
148    async fn invalid_api_key() {
149        let mut server = mockito::Server::new_async().await;
150        let client = Client::<ReqwestExecutor>::builder()
151            .with_api_key("secret".into())
152            .with_base_url(server.url())
153            .build()
154            .unwrap();
155
156        let _m = server
157            .mock("GET", "/tv/1399/aggregate_credits")
158            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
159            .with_status(401)
160            .with_header("content-type", "application/json")
161            .with_body(include_str!("../../assets/invalid-api-key.json"))
162            .create_async()
163            .await;
164
165        let err = TVShowAggregateCredits::new(1399)
166            .execute(&client)
167            .await
168            .unwrap_err();
169        let server_err = err.as_server_error().unwrap();
170        assert_eq!(server_err.status_code, 7);
171    }
172
173    #[tokio::test]
174    async fn resource_not_found() {
175        let mut server = mockito::Server::new_async().await;
176        let client = Client::<ReqwestExecutor>::builder()
177            .with_api_key("secret".into())
178            .with_base_url(server.url())
179            .build()
180            .unwrap();
181
182        let _m = server
183            .mock("GET", "/tv/1399/aggregate_credits")
184            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
185            .with_status(404)
186            .with_header("content-type", "application/json")
187            .with_body(include_str!("../../assets/resource-not-found.json"))
188            .create_async()
189            .await;
190
191        let err = TVShowAggregateCredits::new(1399)
192            .execute(&client)
193            .await
194            .unwrap_err();
195        let server_err = err.as_server_error().unwrap();
196        assert_eq!(server_err.status_code, 34);
197    }
198}
199
200#[cfg(all(test, feature = "integration"))]
201mod integration_tests {
202    use crate::Client;
203    use crate::client::reqwest::ReqwestExecutor;
204    use crate::prelude::Command;
205
206    use super::TVShowAggregateCredits;
207
208    #[tokio::test]
209    async fn execute() {
210        let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
211        let client = Client::<ReqwestExecutor>::new(secret);
212
213        let result = TVShowAggregateCredits::new(1399)
214            .execute(&client)
215            .await
216            .unwrap();
217        assert_eq!(result.id, 1399);
218        assert!(!result.cast.is_empty());
219        assert!(!result.crew.is_empty());
220    }
221}