1pub mod models;
2mod utils;
3
4use std::collections::HashMap;
5use std::fs;
6use std::path::Path;
7
8#[cfg(feature = "log")]
9use log::{trace, warn};
10use models::enchantment::SkyblockEnchantment;
11use models::item::SkyblockItem;
12use models::pet::SkyblockPet;
13pub use models::{UpgradeCost, UpgradeType, enchantment, item, pet, recipe};
14#[cfg(feature = "python")]
15use pyo3::exceptions::PyValueError;
16#[cfg(feature = "python")]
17use pyo3::prelude::*;
18#[cfg(feature = "python")]
19use pyo3::types::PyModule;
20use rustc_hash::FxHashMap;
21use serde::Deserialize;
22pub use utils::{delete_repo, download_repo};
23
24#[cfg(feature = "python")]
25#[pymodule]
26fn skyblock_repo(m: &Bound<'_, PyModule>) -> PyResult<()> {
27	m.add_class::<SkyblockRepo>()?;
28	m.add_function(pyo3::wrap_pyfunction!(download_repo, m)?)?;
29	m.add_function(pyo3::wrap_pyfunction!(delete_repo, m)?)?;
30
31	Ok(())
32}
33
34#[derive(Deserialize)]
35struct RepoStructure {
36	#[allow(dead_code)]
37	version: u8,
38	paths: HashMap<String, String>,
39}
40
41#[cfg_attr(feature = "python", pyclass)]
43pub struct SkyblockRepo {
44	pub enchantments: FxHashMap<String, SkyblockEnchantment>,
45	pub items: FxHashMap<String, SkyblockItem>,
46	pub pets: FxHashMap<String, SkyblockPet>,
47}
48
49#[cfg(feature = "python")]
50#[pymethods]
51impl SkyblockRepo {
52	#[must_use]
62	#[new]
63	pub fn new() -> PyResult<Self> {
64		let structure: RepoStructure =
65			serde_json::from_str(&fs::read_to_string("SkyblockRepo/manifest.json")?)
66				.map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
67
68		let mut repo = Self {
69			enchantments: FxHashMap::default(),
70			items: FxHashMap::default(),
71			pets: FxHashMap::default(),
72		};
73
74		for path_name in structure.paths.values() {
75			let path = &format!("SkyblockRepo/{}", path_name);
76			let path = Path::new(path);
77			let data_entries = fs::read_dir(&path)?;
78
79			for json in data_entries {
80				let json = json?.path();
81				let content = fs::read_to_string(&json)?;
82
83				match path_name.as_str() {
84					| "enchantments" => {
85						let parsed: SkyblockEnchantment = serde_json::from_str(&content)
86							.map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
87						repo.enchantments.insert(parsed.internal_id.clone(), parsed);
88					},
89					| "items" => {
90						let parsed: SkyblockItem = serde_json::from_str(&content)
91							.map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
92						repo.items.insert(parsed.internal_id.clone(), parsed);
93					},
94					| "pets" => {
95						let parsed: SkyblockPet = serde_json::from_str(&content)
96							.map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
97						repo.pets.insert(parsed.internal_id.clone(), parsed);
98					},
99					| _ => continue,
100				}
101			}
102		}
103
104		Ok(repo)
105	}
106
107	#[must_use]
109	#[inline]
110	pub fn get_enchantment_by_id(
111		&self,
112		id: &str,
113	) -> Option<SkyblockEnchantment> {
114		self.enchantments.get(id).cloned()
115	}
116
117	#[must_use]
119	#[inline]
120	pub fn get_item_by_id(
121		&self,
122		id: &str,
123	) -> Option<SkyblockItem> {
124		self.items.get(id).cloned()
125	}
126
127	#[must_use]
129	#[inline]
130	pub fn get_pet_by_id(
131		&self,
132		id: &str,
133	) -> Option<SkyblockPet> {
134		self.pets.get(id).cloned()
135	}
136}
137
138#[cfg(not(feature = "python"))]
139impl SkyblockRepo {
140	pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
141		let structure: RepoStructure =
142			serde_json::from_str(&fs::read_to_string("SkyblockRepo/manifest.json")?)?;
143
144		let mut repo = Self {
145			enchantments: FxHashMap::default(),
146			items: FxHashMap::default(),
147			pets: FxHashMap::default(),
148		};
149
150		for path_name in structure.paths.values() {
151			let path = &format!("SkyblockRepo/{}", path_name);
152			let path = Path::new(path);
153			let data_entries = fs::read_dir(&path)?;
154
155			for json in data_entries {
156				let json = json?.path();
157				#[cfg(feature = "log")]
158				trace!("parsing {:?}", json);
159				let content = fs::read_to_string(&json)?;
160
161				match path_name.as_str() {
162					| "enchantments" => {
163						let parsed: SkyblockEnchantment = serde_json::from_str(&content)?;
164						repo.enchantments.insert(parsed.internal_id.clone(), parsed);
165					},
166					| "items" => {
167						let parsed: SkyblockItem = serde_json::from_str(&content)?;
168						repo.items.insert(parsed.internal_id.clone(), parsed);
169					},
170					| "pets" => {
171						let parsed: SkyblockPet = serde_json::from_str(&content)?;
172						repo.pets.insert(parsed.internal_id.clone(), parsed);
173					},
174					#[cfg_attr(not(feature = "log"), allow(unused_variables))]
175					| other => {
176						#[cfg(feature = "log")]
177						warn!("Unknown dir found while parsing SkyblockData: {}", other);
178						continue;
179					},
180				}
181			}
182		}
183
184		Ok(repo)
185	}
186
187	#[must_use]
189	#[inline]
190	pub fn get_enchantment_by_id(
191		&self,
192		id: &str,
193	) -> Option<SkyblockEnchantment> {
194		self.enchantments.get(id).cloned()
195	}
196
197	#[must_use]
199	#[inline]
200	pub fn get_item_by_id(
201		&self,
202		id: &str,
203	) -> Option<SkyblockItem> {
204		self.items.get(id).cloned()
205	}
206
207	#[must_use]
209	#[inline]
210	pub fn get_pet_by_id(
211		&self,
212		id: &str,
213	) -> Option<SkyblockPet> {
214		self.pets.get(id).cloned()
215	}
216}