1use chrono::{DateTime, Utc};
2use serde::Deserialize;
3use url::Url;
4
5use crate::{Embed, Itchio, ItchioError, parsers::{date_from_str, option_date_from_str}};
6
7#[derive(Clone, Debug, Deserialize)] #[allow(dead_code)]
9struct WrappedGames {
10 games: Vec<Game>,
11 page: u16,
12 per_page: u16,
13}
14
15#[derive(Clone, Debug, Deserialize)]
17pub struct Game {
18 pub id: u32,
19 pub title: String,
20 pub short_text: Option<String>,
21 pub url: Url,
22 pub cover_url: Option<Url>,
23 pub still_cover_url: Option<Url>,
24 pub r#type: String,
25 pub classification: String,
26 pub p_linux: bool,
27 pub p_android: bool,
28 pub p_windows: bool,
29 pub p_osx: bool,
30 #[serde(deserialize_with = "date_from_str")]
31 pub created_at: DateTime<Utc>,
32 pub min_price: u32,
33 pub can_be_bought: bool,
34 #[serde(default, deserialize_with = "option_date_from_str")]
35 pub published_at: Option<DateTime<Utc>>,
36 pub has_demo: bool,
37 pub embed: Option<Embed>,
38 pub in_press_system: bool,
39}
40
41impl Itchio {
42 pub async fn search_games(&self, query: &str, page: u16) -> Result<Vec<Game>, ItchioError> {
44 let endpoint = format!("search/games?query={}&page={}", query, page);
45 let response = self.request::<WrappedGames>(endpoint).await?;
46 Ok(response.games)
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use std::env;
54 use dotenv::dotenv;
55
56 #[tokio::test]
57 async fn good() {
58 dotenv().ok();
59 let client_secret = env::var("KEY").expect("KEY has to be set");
60 let api = Itchio::new(client_secret).unwrap();
61 let search = api.search_games("a", 1).await.inspect_err(|err| eprintln!("Error spotted: {}", err));
62 assert!(search.is_ok())
63 }
64
65 #[tokio::test]
66 async fn bad_key() {
67 let api = Itchio::new("bad_key".to_string()).unwrap();
68 let search = api.search_games("a", 1).await;
69 assert!(search.is_err_and(|err| matches!(err, ItchioError::BadKey)))
70 }
71}