1use thiserror::Error;
3use serde::{Deserialize};
4use url::Url;
5
6pub struct Itchio {
8 client: reqwest::Client,
9 key: String,
10}
11
12#[derive(Clone, Debug, Deserialize)]
14struct Games {
15 games: Vec<Game>
16}
17
18#[derive(Clone, Debug, Deserialize)] #[allow(dead_code)]
20pub struct Game {
21 id: u32,
22 title: String,
23 short_text: Option<String>,
24 url: Url,
25 cover_url: Option<Url>,
26 r#type: String,
27 classification: String,
28 p_linux: bool,
29 p_android: bool,
30 p_windows: bool,
31 p_osx: bool,
32 created_at: String, min_price: u32,
34 can_be_bought: bool,
35 published: bool,
36 published_at: Option<String>, has_demo: bool,
38 embed: Option<Embed>,
39 user: User,
40 views_count: u32,
41 purchases_count: u32,
42 downloads_count: u32,
43 in_press_system: bool,
44}
45
46#[derive(Clone, Debug, Deserialize)] #[allow(dead_code)]
47pub struct User {
48 id: u32,
49 display_name: String,
50 username: String,
51 url: String,
52 cover_url: String,
53}
54
55#[derive(Clone, Debug, Deserialize)] #[allow(dead_code)]
56pub struct Embed {
57 width: u32,
58 height: u32,
59 fullscreen: bool,
60}
61
62impl Itchio {
63 pub fn new(key: String) -> Self {
65 Self {
66 client: reqwest::Client::new(),
67 key,
68 }
69 }
70
71 pub async fn get_my_games(&self) -> Result<Vec<Game>, ItchioError> {
73 let url = format!("https://itch.io/api/1/{}/my-games", self.key);
74 let response = self
75 .client
76 .get(&url)
77 .send()
78 .await?
79 .json::<Games>()
80 .await?;
81
82 Ok(response.games)
83 }
84}
85
86#[derive(Error, Debug)]
88pub enum ItchioError {
89 #[error("HTTP request failed: {0}")]
91 RequestFailed(#[from] reqwest::Error),
92 #[error("Failed to parse JSON: {0}")]
94 JsonParseError(#[from] serde_json::Error),
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use std::env;
101 use dotenv::dotenv;
102
103 #[tokio::test]
104 async fn get_my_games_ok() {
105 dotenv().ok();
106 let client_secret = env::var("KEY").expect("KEY has to be set");
107 let api = Itchio::new(client_secret);
108 let games = api.get_my_games().await;
109 assert!(games.is_ok())
110 }
111
112 #[tokio::test]
113 async fn get_my_games_bad_key() {
114 let api = Itchio::new("bad_key".to_string());
115 let games = api.get_my_games().await;
116 assert!(games.is_err_and(|err| matches!(err, ItchioError::RequestFailed(_))))
117 }
118}