1extern crate hyper;
5extern crate hyper_tls;
6extern crate tokio;
7extern crate futures;
8#[macro_use]
9extern crate serde_derive;
10extern crate failure;
11#[macro_use]
12extern crate failure_derive;
13extern crate serde;
14extern crate serde_json;
15
16use futures::{Future, Stream};
17use std::sync::{Arc, Mutex};
18use std::borrow::Cow;
19
20#[derive(Debug, Fail)]
23pub enum FetchError {
24 #[fail(display = "HTTP error: {}", _0)]
25 Http(hyper::Error),
26 #[fail(display = "JSON parsing error: {}", _0)]
27 Json(serde_json::Error),
28 #[fail(display = "{}", _0)]
29 NotFound(String),
30}
31
32impl From<hyper::Error> for FetchError {
33 fn from(err: hyper::Error) -> FetchError {
34 FetchError::Http(err)
35 }
36}
37
38impl From<serde_json::Error> for FetchError {
39 fn from(err: serde_json::Error) -> FetchError {
40 FetchError::Json(err)
41 }
42}
43
44pub trait Request {
46 type Response: serde::de::DeserializeOwned + std::fmt::Debug + std::marker::Send;
47
48 fn url(&self) -> Cow<'static, str>;
50}
51
52#[derive(Deserialize, Debug)]
54pub struct SerieTraduzione {
55 id_serie: usize,
56 nome_serie: String,
57 link_serie: String,
58 id_thetvdb: usize,
59 num_stagione: usize,
60 num_episodio: usize,
61 stato: String,
62}
63
64pub struct ReqSerieTraduzione;
66
67impl Request for ReqSerieTraduzione {
68 type Response = SerieTraduzione;
69
70 fn url(&self) -> Cow<'static, str> {
71 Cow::Borrowed("https://www.subspedia.tv/API/serie_traduzione")
72 }
73}
74
75#[derive(Deserialize, Debug, Clone)]
77pub struct Serie {
78 id_serie: usize,
79 pub nome_serie: String,
80 link_serie: String,
81 id_thetvdb: usize,
82 stato: String,
83 anno: usize,
84}
85
86pub struct ReqElencoSerie;
88
89impl Request for ReqElencoSerie {
90 type Response = Serie;
91
92 fn url(&self) -> Cow<'static, str> {
93 Cow::Borrowed("https://www.subspedia.tv/API/elenco_serie")
94 }
95}
96
97#[derive(Deserialize, Debug)]
99pub struct Sottotitolo {
100 id_serie: usize,
101 nome_serie: String,
102 ep_titolo: String,
103 num_stagione: usize,
104 num_episodio: usize,
105 immagine: String,
106 link_sottotitoli: String,
107 link_serie: String,
108 link_file: String,
109 descrizione: String,
110 id_thetvdb: usize,
111 data_uscita: String,
112 grazie: usize,
113}
114
115pub struct ReqUltimiSottotitoli;
117
118impl Request for ReqUltimiSottotitoli {
119 type Response = Sottotitolo;
120
121 fn url(&self) -> Cow<'static, str> {
122 Cow::Borrowed("https://www.subspedia.tv/API/ultimi_sottotitoli")
123 }
124}
125
126pub struct ReqSottotitoliSerie {
128 id: usize,
129}
130
131impl ReqSottotitoliSerie {
132 pub fn new(id: usize) -> ReqSottotitoliSerie {
134 ReqSottotitoliSerie { id }
135 }
136}
137
138impl Request for ReqSottotitoliSerie {
139 type Response = Sottotitolo;
140
141 fn url(&self) -> Cow<'static, str> {
142 Cow::Owned(format!("https://www.subspedia.tv/API/sottotitoli_serie?serie={}", self.id))
143 }
144}
145
146pub fn get<R: 'static + Request>(req: &R) -> Result<Vec<R::Response>, FetchError>
164{
165 let url = req.url().parse().unwrap();
166 let result = Arc::new(Mutex::new(Vec::new()));
167
168 let tmp = Arc::clone(&result);
169
170 tokio::run(futures::lazy(move || {
171 fetch_json::<R::Response>(url)
172 .map(move |mut serie| {
174 tmp.lock().unwrap().append(&mut serie);
175 })
176 .map_err(|e| eprintln!("{}", e))
178 }));
179
180 Ok(Arc::try_unwrap(result).unwrap().into_inner().unwrap())
181}
182
183pub fn search_by_name(name: &str) -> Result<Vec<Serie>, FetchError> {
203 let result = get(&ReqElencoSerie)?
204 .iter()
205 .filter(|s| s.nome_serie
206 .to_lowercase()
207 .as_str()
208 .contains(name.to_lowercase().as_str())
209 )
210 .collect::<Vec<_>>()
211 .iter()
212 .map(|s| (**s).clone())
213 .collect::<Vec<_>>();
214
215 if !result.is_empty() {
216 Ok(result)
217 } else {
218 Err(FetchError::NotFound(format!("Series with name {} not found", name)))
219 }
220}
221
222pub fn search_by_id(id: usize) -> Result<Serie, FetchError> {
242 match get(&ReqElencoSerie)?
243 .iter()
244 .filter(|s| s.id_serie == id)
245 .collect::<Vec<_>>()
246 .pop() {
247 Some(s) => Ok(s.clone()),
248 None => Err(FetchError::NotFound(format!("Series with id {} not found.", id)))
249 }
250}
251
252fn fetch_json<T>(url: hyper::Uri) -> impl Future<Item=Vec<T>, Error=FetchError>
253 where T: serde::de::DeserializeOwned + std::fmt::Debug
254{
255 let https = hyper_tls::HttpsConnector::new(4).unwrap();
256 let client = hyper::Client::builder()
257 .build::<_, hyper::Body>(https);
258
259 client
260 .get(url)
262 .and_then(|res| {
264 res.into_body().concat2()
266 })
267 .from_err::<FetchError>()
268 .and_then(|body| {
270 let serie = serde_json::from_slice(&body)?;
272 Ok(serie)
273 })
274}