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 #[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 #[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 #[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 #[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 #[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 #[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 #[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}