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