use chrono::{DateTime, Utc};
use serde::Deserialize;
use url::Url;
use crate::{Embed, Itchio, ItchioError, SmallUser, parsers::{date_from_str, option_date_from_str, empty_object_as_empty_array, small_date_from_str}};
#[derive(Clone, Debug, Deserialize)]
struct Games {
games: Vec<Game>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Game {
pub id: u32,
pub title: String,
pub short_text: Option<String>,
pub url: Url,
pub cover_url: Option<Url>,
pub still_cover_url: Option<Url>,
pub r#type: String,
pub classification: String,
pub p_linux: bool,
pub p_android: bool,
pub p_windows: bool,
pub p_osx: bool,
#[serde(deserialize_with = "date_from_str")]
pub created_at: DateTime<Utc>,
pub min_price: u32,
pub can_be_bought: bool,
pub published: bool,
#[serde(default, deserialize_with = "option_date_from_str")]
pub published_at: Option<DateTime<Utc>>,
pub has_demo: bool,
pub embed: Option<Embed>,
pub user: SmallUser,
pub views_count: u64,
pub purchases_count: u64,
pub downloads_count: u64,
pub in_press_system: bool,
pub earnings: Option<Vec<Earning>>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Earning {
pub currency: String,
pub amount_formatted: String,
pub amount: u64,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Graphs {
#[serde(deserialize_with = "empty_object_as_empty_array")]
pub downloads: Vec<Download>,
#[serde(deserialize_with = "empty_object_as_empty_array")]
pub purchases: Vec<Purchase>,
#[serde(deserialize_with = "empty_object_as_empty_array")]
pub views: Vec<View>,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Download {}
#[derive(Clone, Debug, Deserialize)]
pub struct Purchase {}
#[derive(Clone, Debug, Deserialize)]
pub struct View {
#[serde(deserialize_with = "small_date_from_str")]
pub date: DateTime<Utc>,
pub count: u64,
}
impl Itchio {
pub async fn get_my_games(&self) -> Result<Vec<Game>, ItchioError> {
let response = self.request::<Games>("my-games".to_string()).await?;
Ok(response.games)
}
pub async fn get_my_games_graphs(&self) -> Result<Graphs, ItchioError> {
Ok(self.request::<Graphs>("my-games/graphs".to_string()).await?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use dotenv::dotenv;
#[tokio::test]
async fn good_games() {
dotenv().ok();
let client_secret = env::var("KEY").expect("KEY has to be set");
let api = Itchio::new(client_secret).unwrap();
let games = api.get_my_games().await.inspect_err(|err| eprintln!("Error spotted: {}", err));
assert!(games.is_ok())
}
#[tokio::test]
async fn bad_key_games() {
let api = Itchio::new("bad_key".to_string()).unwrap();
let games = api.get_my_games().await;
assert!(games.is_err_and(|err| matches!(err, ItchioError::BadKey)))
}
#[tokio::test]
async fn good_graphs() {
dotenv().ok();
let client_secret = env::var("KEY").expect("KEY has to be set");
let api = Itchio::new(client_secret).unwrap();
let graphs = api.get_my_games_graphs().await.inspect_err(|err| eprintln!("Error spotted: {}", err));
assert!(graphs.is_ok())
}
#[tokio::test]
async fn bad_key_graphs() {
let api = Itchio::new("bad_key".to_string()).unwrap();
let graphs = api.get_my_games_graphs().await.inspect_err(|err| eprintln!("Error spotted: {}", err));
assert!(graphs.is_err_and(|err| matches!(err, ItchioError::BadKey)))
}
}