Skip to main content

itchio_api/
my_games.rs

1use chrono::{DateTime, Utc};
2use serde::Deserialize;
3use url::Url;
4
5use crate::{Embed, Itchio, ItchioError, SmallUser, parsers::{date_from_str, option_date_from_str, empty_object_as_empty_array, small_date_from_str}};
6
7/// The original response this crate gets from the server, useless to crate users.
8#[derive(Clone, Debug, Deserialize)]
9struct Games {
10  games: Vec<Game>,
11}
12
13/// A representation of one of yours Games on the itch.io website.
14#[derive(Clone, Debug, Deserialize)]
15pub struct Game {
16  pub id: u32,
17  pub title: String,
18  pub short_text: Option<String>,
19  pub url: Url,
20  pub cover_url: Option<Url>,
21  pub still_cover_url: Option<Url>,
22  pub r#type: String,
23  pub classification: String,
24  pub p_linux: bool,
25  pub p_android: bool,
26  pub p_windows: bool,
27  pub p_osx: bool,
28  #[serde(deserialize_with = "date_from_str")]
29  pub created_at: DateTime<Utc>,
30  pub min_price: u32,
31  pub can_be_bought: bool,
32  pub published: bool,
33  // Note to self: When using a custom deserializer function, if the property is an Option because it might be missing in the JSON,
34  // "default" needs to be added to cover the missing field case
35  #[serde(default, deserialize_with = "option_date_from_str")]
36  pub published_at: Option<DateTime<Utc>>,
37  pub has_demo: bool,
38  pub embed: Option<Embed>,
39  pub user: SmallUser,
40  pub views_count: u64,
41  pub purchases_count: u64,
42  pub downloads_count: u64,
43  pub in_press_system: bool,
44  pub earnings: Option<Vec<Earning>>,
45}
46
47/// How much money a Game has earned its developers.
48#[derive(Clone, Debug, Deserialize)]
49pub struct Earning {
50  pub currency: String,
51  pub amount_formatted: String,
52  pub amount: u64,
53}
54
55/// Recent stats about a Game.
56#[derive(Clone, Debug, Deserialize)]
57pub struct Graphs {
58  #[serde(deserialize_with = "empty_object_as_empty_array")]
59  pub downloads: Vec<Download>,
60  #[serde(deserialize_with = "empty_object_as_empty_array")]
61  pub purchases: Vec<Purchase>,
62  #[serde(deserialize_with = "empty_object_as_empty_array")]
63  pub views: Vec<View>,
64}
65
66/// TODO no idea what this is supposed to be
67#[derive(Clone, Debug, Deserialize)]
68pub struct Download {}
69
70/// TODO no idea what this is supposed to be
71#[derive(Clone, Debug, Deserialize)]
72pub struct Purchase {}
73
74/// A representation of how many times a Game has been viewed in one day.
75#[derive(Clone, Debug, Deserialize)]
76pub struct View {
77  #[serde(deserialize_with = "small_date_from_str")]
78  pub date: DateTime<Utc>,
79  pub count: u64,
80}
81
82impl Itchio {
83  /// Get the games you've uploaded or have edit access to: <https://itch.io/docs/api/serverside#reference/profilegames-httpsitchioapi1keymy-games>
84  pub async fn get_my_games(&self) -> Result<Vec<Game>, ItchioError> {
85    let response = self.request::<Games>("my-games".to_string()).await?;
86    Ok(response.games)
87  }
88
89  /// Get the views, downloads, and purchases related to your games.
90  pub async fn get_my_games_graphs(&self) -> Result<Graphs, ItchioError> {
91    Ok(self.request::<Graphs>("my-games/graphs".to_string()).await?)
92  }
93}
94
95#[cfg(test)]
96mod tests {
97  use super::*;
98  use std::env;
99  use dotenv::dotenv;
100
101  #[tokio::test]
102  async fn good_games() {
103    dotenv().ok();
104    let client_secret = env::var("KEY").expect("KEY has to be set");
105    let api = Itchio::new(client_secret).unwrap();
106    let games = api.get_my_games().await.inspect_err(|err| eprintln!("Error spotted: {}", err));
107    assert!(games.is_ok())
108  }
109
110  #[tokio::test]
111  async fn bad_key_games() {
112    let api = Itchio::new("bad_key".to_string()).unwrap();
113    let games = api.get_my_games().await;
114    assert!(games.is_err_and(|err| matches!(err, ItchioError::BadKey)))
115  }
116
117  #[tokio::test]
118  async fn good_graphs() {
119    dotenv().ok();
120    let client_secret = env::var("KEY").expect("KEY has to be set");
121    let api = Itchio::new(client_secret).unwrap();
122    let graphs = api.get_my_games_graphs().await.inspect_err(|err| eprintln!("Error spotted: {}", err));
123    assert!(graphs.is_ok())
124  }
125
126  #[tokio::test]
127  async fn bad_key_graphs() {
128    let api = Itchio::new("bad_key".to_string()).unwrap();
129    let graphs = api.get_my_games_graphs().await.inspect_err(|err| eprintln!("Error spotted: {}", err));
130    assert!(graphs.is_err_and(|err| matches!(err, ItchioError::BadKey)))
131  }
132}