skyblock_repo/
lib.rs

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/// each category of items as a mapping of `internal_id` to its item data
42#[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	/// Creates HashMaps for each category
53	///
54	/// Throws warning log if it encounters a category it did not expect
55	///
56	/// Requires that the `SkyblockRepo` directory exists, which you can create via
57	///
58	/// ```rust
59	/// skyblock_repo::download_repo(true);
60	/// ```
61	#[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				#[cfg(feature = "log")]
82				trace!("parsing {:?}", json);
83				let content = fs::read_to_string(&json)?;
84
85				match path_name.as_str() {
86					| "enchantments" => {
87						let parsed: SkyblockEnchantment = serde_json::from_str(&content)
88							.map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
89						repo.enchantments.insert(parsed.internal_id.clone(), parsed);
90					},
91					| "items" => {
92						let parsed: SkyblockItem = serde_json::from_str(&content)
93							.map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
94						repo.items.insert(parsed.internal_id.clone(), parsed);
95					},
96					| "pets" => {
97						let parsed: SkyblockPet = serde_json::from_str(&content)
98							.map_err(|e| PyErr::new::<PyValueError, _>(e.to_string()))?;
99						repo.pets.insert(parsed.internal_id.clone(), parsed);
100					},
101					#[cfg_attr(not(feature = "log"), allow(unused_variables))]
102					| other => {
103						#[cfg(feature = "log")]
104						warn!("Unknown dir found while parsing SkyblockData: {}", other);
105						continue;
106					},
107				}
108			}
109		}
110
111		Ok(repo)
112	}
113
114	/// Retrieves an enchantment by its `internalId`
115	#[must_use]
116	#[inline]
117	pub fn get_enchantment_by_id(
118		&self,
119		id: &str,
120	) -> Option<SkyblockEnchantment> {
121		self.enchantments.get(id).cloned()
122	}
123
124	/// Retrieves an item by its `internalId`
125	#[must_use]
126	#[inline]
127	pub fn get_item_by_id(
128		&self,
129		id: &str,
130	) -> Option<SkyblockItem> {
131		self.items.get(id).cloned()
132	}
133
134	/// Retrieves a pet by its `internalId`
135	#[must_use]
136	#[inline]
137	pub fn get_pet_by_id(
138		&self,
139		id: &str,
140	) -> Option<SkyblockPet> {
141		self.pets.get(id).cloned()
142	}
143}
144
145#[cfg(not(feature = "python"))]
146impl SkyblockRepo {
147	pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
148		let structure: RepoStructure =
149			serde_json::from_str(&fs::read_to_string("SkyblockRepo/manifest.json")?)?;
150
151		let mut repo = Self {
152			enchantments: FxHashMap::default(),
153			items: FxHashMap::default(),
154			pets: FxHashMap::default(),
155		};
156
157		for path_name in structure.paths.values() {
158			let path = &format!("SkyblockRepo/{}", path_name);
159			let path = Path::new(path);
160			let data_entries = fs::read_dir(&path)?;
161
162			for json in data_entries {
163				let json = json?.path();
164				#[cfg(feature = "log")]
165				trace!("parsing {:?}", json);
166				let content = fs::read_to_string(&json)?;
167
168				match path_name.as_str() {
169					| "enchantments" => {
170						let parsed: SkyblockEnchantment = serde_json::from_str(&content)?;
171						repo.enchantments.insert(parsed.internal_id.clone(), parsed);
172					},
173					| "items" => {
174						let parsed: SkyblockItem = serde_json::from_str(&content)?;
175						repo.items.insert(parsed.internal_id.clone(), parsed);
176					},
177					| "pets" => {
178						let parsed: SkyblockPet = serde_json::from_str(&content)?;
179						repo.pets.insert(parsed.internal_id.clone(), parsed);
180					},
181					#[cfg_attr(not(feature = "log"), allow(unused_variables))]
182					| other => {
183						#[cfg(feature = "log")]
184						warn!("Unknown dir found while parsing SkyblockData: {}", other);
185						continue;
186					},
187				}
188			}
189		}
190
191		Ok(repo)
192	}
193
194	/// Retrieves an enchantment by its `internalId`
195	#[must_use]
196	#[inline]
197	pub fn get_enchantment_by_id(
198		&self,
199		id: &str,
200	) -> Option<SkyblockEnchantment> {
201		self.enchantments.get(id).cloned()
202	}
203
204	/// Retrieves an item by its `internalId`
205	#[must_use]
206	#[inline]
207	pub fn get_item_by_id(
208		&self,
209		id: &str,
210	) -> Option<SkyblockItem> {
211		self.items.get(id).cloned()
212	}
213
214	/// Retrieves a pet by its `internalId`
215	#[must_use]
216	#[inline]
217	pub fn get_pet_by_id(
218		&self,
219		id: &str,
220	) -> Option<SkyblockPet> {
221		self.pets.get(id).cloned()
222	}
223}