1use std::collections::HashMap;
33use futures::future::join_all;
34use itertools::Itertools;
35use serde::Deserialize;
36use strum_macros::{Display, EnumIter};
37use strum::IntoEnumIterator;
38use rayon::prelude::*;
39use crate::error::Error;
40
41#[derive(Display, Debug, EnumIter)]
43pub enum Region {
44 GB,
45 JP,
46 KR,
47 TW,
48 US
49}
50
51#[derive(Deserialize, Eq, PartialEq, Debug)]
53pub struct Release {
54 #[serde(alias = "Name")]
55 pub name: String,
56 #[serde(alias = "UID")]
57 pub uid: String,
58 #[serde(alias = "TitleID")]
59 pub title_id: String,
60 #[serde(alias = "Version")]
61 pub version: String,
62 #[serde(alias = "Size")]
63 pub size: String,
64 #[serde(alias = "Product Code")]
65 pub product_code: String,
66 #[serde(alias = "Publisher")]
67 pub publisher: String
68}
69
70pub async fn get_all_releases() -> Result<Vec<Release>, Error> {
72 let release_futures = Region::iter().map(|region| get_releases_async(region));
73 let releases = join_all(release_futures).await;
74 releases.into_iter().flatten_ok().collect()
75}
76
77pub async fn get_releases_async(region: Region) -> Result<Vec<Release>, Error> {
79 let request = reqwest::get(&format!("https://raw.githubusercontent.com/hax0kartik/3dsdb/master/jsons/list_{}.json", region)).await?;
80 match request.json().await {
81 Ok(releases) => Ok(releases),
82 Err(error) => Err(Error::from(error))
83 }
84}
85
86pub fn get_releases(region: Region) -> Result<Vec<Release>, Error> {
88 let request = reqwest::blocking::get(&format!("https://raw.githubusercontent.com/hax0kartik/3dsdb/master/jsons/list_{}.json", region))?;
89 match request.json() {
90 Ok(releases) => Ok(releases),
91 Err(error) => Err(Error::from(error))
92 }
93}
94
95pub fn get_releases_map(region: Region) -> Result<HashMap<String, Release>, Error> {
105 let releases = get_releases(region)?;
106 Ok(releases.into_par_iter()
107 .map(|release| (release.title_id.clone(), release))
108 .collect())
109}
110
111#[cfg(test)]
112mod tests {
113 use std::ops::Deref;
114 use once_cell::sync::Lazy;
115 use rstest::*;
116 use crate::json::{get_all_releases, get_releases, get_releases_async, get_releases_map, Region, Release};
117
118 #[rstest]
119 #[case(Region::GB, "GB")]
120 #[case(Region::JP, "JP")]
121 #[case(Region::KR, "KR")]
122 #[case(Region::TW, "TW")]
123 #[case(Region::US, "US")]
124 fn region_to_string_outputs_correct_string(#[case] region: Region, #[case] expected: String) {
125 let actual = format!("{}", region);
126 assert_eq!(actual, expected)
127 }
128
129 static EXPECTED_RELEASE: Lazy<Release> = Lazy::new(|| Release {
130 name: "Shovel Software Insurance Claim".to_string(),
131 uid: "50010000049535".to_string(),
132 title_id: "000400000F715C00".to_string(),
133 version: "N/A".to_string(),
134 size: "25.7 MB [206 blocks]".to_string(),
135 product_code: "KTR-N-CF6P".to_string(),
136 publisher: "Batafurai".to_string()
137 });
138
139 #[rstest]
140 async fn get_all_releases_returns_valid_information() {
141 let releases = get_all_releases().await.unwrap();
142 let actual = releases.get(0).unwrap();
143 assert_eq!(actual, EXPECTED_RELEASE.deref())
144 }
145
146 #[rstest]
147 fn get_releases_returns_valid_information() {
148 let releases = get_releases(Region::GB).unwrap();
149 let actual = releases.get(0).unwrap();
150 assert_eq!(actual, EXPECTED_RELEASE.deref())
151 }
152
153 #[rstest]
154 async fn get_releases_async_returns_valid_information() {
155 let releases = get_releases_async(Region::GB).await.unwrap();
156 let actual = releases.get(0).unwrap();
157 assert_eq!(actual, EXPECTED_RELEASE.deref())
158 }
159
160 #[rstest]
161 fn get_releases_map_returns_valid_information() {
162 let releases = get_releases_map(Region::GB).unwrap();
163 let actual = releases.get(&EXPECTED_RELEASE.title_id).unwrap();
164 assert_eq!(actual, EXPECTED_RELEASE.deref())
165 }
166}