1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
use hyper::{body::HttpBody, Client}; use hyper_tls::HttpsConnector; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum Status { Ok, Other(String), } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct Torrent { pub url: String, pub hash: String, pub quality: String, #[serde(rename = "type")] pub _type: String, pub seeds: u32, pub peers: u32, pub size: String, pub size_bytes: u64, pub date_uploaded: String, pub date_uploaded_unix: u64, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Movie { pub id: u32, pub url: String, pub imdb_code: String, pub title: String, pub title_english: String, pub title_long: String, pub slug: String, pub year: u32, pub rating: f32, pub runtime: u32, pub genres: Vec<String>, pub summary: String, pub description_full: String, pub synopsis: String, pub yt_trailer_code: String, pub language: String, pub mpa_rating: String, pub background_image: String, pub background_image_original: String, pub small_cover_image: String, pub medium_cover_image: String, pub large_cover_image: String, pub state: Status, pub torrents: Vec<Torrent>, pub date_uploaded: String, pub date_uploaded_unix: u64, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct MovieList { pub movie_count: u32, pub limit: u32, pub page_number: u32, #[serde(skip_serializing_if = "Vec::is_empty", default)] pub movies: Vec<Movie>, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(untagged)] pub enum Data { MovieList(MovieList), } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct Response { pub status: Status, pub status_message: String, pub data: Option<Data>, } pub async fn list_movies( query_term: &str, ) -> Result<MovieList, Box<dyn std::error::Error + Send + Sync>> { let https = HttpsConnector::new(); let client = Client::builder().build::<_, hyper::Body>(https); let url = format!( "https://yts.mx/api/v2/list_movies.json?query_term={}", query_term ); let mut res = client.get(url.parse()?).await?; let mut bytes = Vec::new(); while let Some(next) = res.data().await { let chunk = next?; bytes.extend(chunk); } let body = String::from_utf8(bytes)?; let response: Response = serde_json::from_str(&body)?; if let Status::Other(status) = response.status { return Err(format!("{}: {}", status, response.status_message).into()); } let data = response.data.ok_or("Data missing")?; match data { Data::MovieList(movie_list) => Ok(movie_list), } } #[cfg(test)] mod tests { static TEST_DATA: &str = include_str!("test/test.json"); use super::*; #[test] fn deserialize_test_data() { let response: Response = serde_json::from_str(TEST_DATA).unwrap(); assert_eq!(response.status, Status::Ok); assert_eq!(response.status_message, "Query was successful"); let data = response.data.unwrap(); let movie_list = match data { Data::MovieList(movie_list) => movie_list, }; assert_eq!(movie_list.movie_count, 10); assert_eq!(movie_list.limit, 20); assert_eq!(movie_list.page_number, 1); assert_eq!(movie_list.movies.len(), 10); } #[test] fn deserialize_empty_test_data() { static TEST_DATA: &str = include_str!("test/test_empty.json"); let response: Response = serde_json::from_str(TEST_DATA).unwrap(); assert_eq!(response.status, Status::Ok); assert_eq!(response.status_message, "Query was successful"); let data = response.data.unwrap(); let movie_list = match data { Data::MovieList(movie_list) => movie_list, }; assert_eq!(movie_list.movie_count, 0); assert_eq!(movie_list.limit, 20); assert_eq!(movie_list.page_number, 1); assert_eq!(movie_list.movies.len(), 0); } }