1pub mod models;
2mod utils;
3
4use std::collections::HashMap;
5use std::fs;
6use std::path::Path;
7
8#[cfg(all(feature = "log", not(feature = "python")))]
9use log::{trace, warn};
10use models::enchantment::SkyblockEnchantment;
11use models::item::SkyblockItem;
12use models::npc::SkyblockNpc;
13use models::pet::SkyblockPet;
14use models::shop::SkyblockShop;
15use models::zone::SkyblockZone;
16pub use models::{UpgradeCost, UpgradeType, enchantment, item, pet, recipe};
17#[cfg(feature = "python")]
18use pyo3::exceptions::PyValueError;
19#[cfg(feature = "python")]
20use pyo3::prelude::*;
21#[cfg(feature = "python")]
22use pyo3::types::PyModule;
23use rustc_hash::FxHashMap;
24use serde::Deserialize;
25#[cfg(not(feature = "python"))]
26use skyblock_repo_macros::getter;
27pub use utils::{delete_repo, download_repo};
28
29#[cfg(feature = "python")]
30#[pymodule]
31fn skyblock_repo(m: &Bound<'_, PyModule>) -> PyResult<()> {
32 m.add_class::<SkyblockRepo>()?;
33 m.add_function(pyo3::wrap_pyfunction!(download_repo, m)?)?;
34 m.add_function(pyo3::wrap_pyfunction!(delete_repo, m)?)?;
35
36 Ok(())
37}
38
39#[derive(Deserialize)]
40struct RepoStructure {
41 #[allow(dead_code)]
42 version: u8,
43 paths: HashMap<String, String>,
44}
45
46#[cfg_attr(feature = "python", pyclass)]
48pub struct SkyblockRepo {
49 pub enchantments: FxHashMap<String, SkyblockEnchantment>,
50 pub items: FxHashMap<String, SkyblockItem>,
51 pub npcs: FxHashMap<String, SkyblockNpc>,
52 pub pets: FxHashMap<String, SkyblockPet>,
53 pub shops: FxHashMap<String, SkyblockShop>,
54 pub zones: FxHashMap<String, SkyblockZone>,
55}
56
57#[cfg(feature = "python")]
58#[pymethods]
59impl SkyblockRepo {
60 #[must_use]
70 #[new]
71 pub fn new() -> PyResult<Self> {
72 let structure: RepoStructure =
73 serde_json::from_str(&fs::read_to_string("SkyblockRepo/manifest.json")?)
74 .map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
75
76 let mut repo = Self {
77 enchantments: FxHashMap::default(),
78 items: FxHashMap::default(),
79 npcs: FxHashMap::default(),
80 pets: FxHashMap::default(),
81 shops: FxHashMap::default(),
82 zones: FxHashMap::default(),
83 };
84
85 for path_name in structure.paths.values() {
86 let path = &format!("SkyblockRepo/{}", path_name);
87 let path = Path::new(path);
88 let data_entries = fs::read_dir(&path)?;
89
90 for json in data_entries {
91 let json = json?.path();
92 let content = fs::read_to_string(&json)?;
93
94 match path_name.as_str() {
95 | "enchantments" => {
96 let parsed: SkyblockEnchantment = serde_json::from_str(&content)
97 .map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
98 repo.enchantments.insert(parsed.internal_id.clone(), parsed);
99 },
100 | "items" => {
101 let parsed: SkyblockItem = serde_json::from_str(&content)
102 .map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
103 repo.items.insert(parsed.internal_id.clone(), parsed);
104 },
105 | "npcs" => {
106 let parsed: SkyblockNpc = serde_json::from_str(&content)
107 .map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
108 repo.npcs.insert(parsed.internal_id.clone(), parsed);
109 },
110 | "pets" => {
111 let parsed: SkyblockPet = serde_json::from_str(&content)
112 .map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
113 repo.pets.insert(parsed.internal_id.clone(), parsed);
114 },
115 | "shops" => {
116 let parsed: SkyblockShop = serde_json::from_str(&content)
117 .map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
118 repo.shops.insert(parsed.internal_id.clone(), parsed);
119 },
120 | "zones" => {
121 let parsed: SkyblockZone = serde_json::from_str(&content)
122 .map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
123 repo.zones.insert(parsed.internal_id.clone(), parsed);
124 },
125 | _ => continue,
126 }
127 }
128 }
129
130 Ok(repo)
131 }
132
133 #[must_use]
135 #[inline]
136 pub fn get_enchantment_by_id(
137 &self,
138 id: &str,
139 ) -> Option<SkyblockEnchantment> {
140 self.enchantments.get(&id.to_uppercase()).cloned()
141 }
142
143 #[must_use]
145 #[inline]
146 pub fn get_item_by_id(
147 &self,
148 id: &str,
149 ) -> Option<SkyblockItem> {
150 self.items.get(&id.to_uppercase()).cloned()
151 }
152
153 #[must_use]
155 #[inline]
156 pub fn get_npc_by_id(
157 &self,
158 id: &str,
159 ) -> Option<SkyblockNpc> {
160 self.npcs.get(&id.to_uppercase()).cloned()
161 }
162
163 #[must_use]
165 #[inline]
166 pub fn get_pet_by_id(
167 &self,
168 id: &str,
169 ) -> Option<SkyblockPet> {
170 self.pets.get(&id.to_uppercase()).cloned()
171 }
172
173 #[must_use]
175 #[inline]
176 pub fn get_shop_by_id(
177 &self,
178 id: &str,
179 ) -> Option<SkyblockShop> {
180 self.shops.get(&id.to_uppercase()).cloned()
181 }
182
183 #[must_use]
185 #[inline]
186 pub fn get_zone_by_id(
187 &self,
188 id: &str,
189 ) -> Option<SkyblockZone> {
190 self.zones.get(&id.to_uppercase()).cloned()
191 }
192}
193
194#[cfg(not(feature = "python"))]
195impl SkyblockRepo {
196 pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
197 let structure: RepoStructure =
198 serde_json::from_str(&fs::read_to_string("SkyblockRepo/manifest.json")?)?;
199
200 let mut repo = Self {
201 enchantments: FxHashMap::default(),
202 items: FxHashMap::default(),
203 npcs: FxHashMap::default(),
204 pets: FxHashMap::default(),
205 shops: FxHashMap::default(),
206 zones: FxHashMap::default(),
207 };
208
209 for path_name in structure.paths.values() {
210 let path = &format!("SkyblockRepo/{}", path_name);
211 let path = Path::new(path);
212 let data_entries = fs::read_dir(&path)?;
213
214 for json in data_entries {
215 let json = json?.path();
216 #[cfg(feature = "log")]
217 trace!("parsing {:?}", json);
218 let content = fs::read_to_string(&json)?;
219
220 match path_name.as_str() {
221 | "enchantments" => {
222 let parsed: SkyblockEnchantment = serde_json::from_str(&content)?;
223 repo.enchantments.insert(parsed.internal_id.clone(), parsed);
224 },
225 | "items" => {
226 let parsed: SkyblockItem = serde_json::from_str(&content)?;
227 repo.items.insert(parsed.internal_id.clone(), parsed);
228 },
229 | "npcs" => {
230 let parsed: SkyblockNpc = serde_json::from_str(&content)?;
231 repo.npcs.insert(parsed.internal_id.clone(), parsed);
232 },
233 | "pets" => {
234 let parsed: SkyblockPet = serde_json::from_str(&content)?;
235 repo.pets.insert(parsed.internal_id.clone(), parsed);
236 },
237 | "shops" => {
238 let parsed: SkyblockShop = serde_json::from_str(&content)?;
239 repo.shops.insert(parsed.internal_id.clone(), parsed);
240 },
241 | "zones" => {
242 let parsed: SkyblockZone = serde_json::from_str(&content)?;
243 repo.zones.insert(parsed.internal_id.clone(), parsed);
244 },
245 #[cfg_attr(not(feature = "log"), allow(unused_variables))]
246 | other => {
247 #[cfg(feature = "log")]
248 warn!("Unknown dir found while parsing SkyblockData: {}", other);
249 continue;
250 },
251 }
252 }
253 }
254
255 Ok(repo)
256 }
257
258 getter!(enchantment);
259 getter!(item);
260 getter!(pet);
261 getter!(npc);
262 getter!(shop);
263 getter!(zone);
264}