1use crate::config::validation_error::ConfigValidationError;
2use crate::config::validation_report::ConfigValidationReport;
3use crate::data::encounter_data::EncounterData;
4use crate::data::item_data::ItemData;
5use crate::data::location_data::LocationData;
6use crate::data::settings::Settings;
7use crate::data::species_data::SpeciesData;
8use crate::enums::item_category::ItemCategory;
9use crate::models::item::attributes::ItemAttributesType;
10use crate::models::item::attributes_container::ItemAttributesContainerInterface;
11use crate::models::item::properties::ItemPropertiesType;
12use crate::models::item::properties_container::ItemPropertiesContainerInterface;
13use std::collections::HashMap;
14use std::fmt::Debug;
15use std::fs::File;
16use std::path::Path;
17use std::sync::Arc;
18
19mod validation_error;
20mod validation_report;
21
22pub trait ConfigInterface: Debug + Send + Sync {
23 fn species(&self) -> Arc<HashMap<i32, Arc<SpeciesData>>>;
24 fn locations(&self) -> Arc<HashMap<i32, Arc<LocationData>>>;
25 fn items(&self) -> Arc<HashMap<i32, Arc<ItemData>>>;
26 fn settings(&self) -> Arc<Settings>;
27 fn species_names(&self) -> Arc<HashMap<i32, String>>;
28 fn location_names(&self) -> Arc<HashMap<i32, String>>;
29 fn item_names(&self) -> Arc<HashMap<i32, String>>;
30 fn items_by_attributes(&self) -> Arc<HashMap<ItemAttributesType, Arc<Vec<Arc<ItemData>>>>>;
31 fn items_by_properties(&self) -> Arc<HashMap<ItemPropertiesType, Arc<Vec<Arc<ItemData>>>>>;
32 fn items_by_category(&self) -> Arc<HashMap<ItemCategory, Arc<Vec<Arc<ItemData>>>>>;
33 fn item_ids_by_category(&self) -> Arc<HashMap<ItemCategory, Arc<Vec<i32>>>>;
34
35 fn get_species_data(&self, species_id: i32) -> Option<Arc<SpeciesData>> {
36 self.species().get(&species_id).cloned()
37 }
38
39 fn get_location_data(&self, location_id: i32) -> Option<Arc<LocationData>> {
40 self.locations().get(&location_id).cloned()
41 }
42
43 fn get_item_data(&self, item_id: i32) -> Option<Arc<ItemData>> {
44 self.items().get(&item_id).cloned()
45 }
46
47 fn get_items_by_attributes_type(
48 &self,
49 attributes_type: ItemAttributesType,
50 ) -> Option<Arc<Vec<Arc<ItemData>>>> {
51 self.items_by_attributes().get(&attributes_type).cloned()
52 }
53
54 fn get_items_by_properties_type(
55 &self,
56 properties_type: ItemPropertiesType,
57 ) -> Option<Arc<Vec<Arc<ItemData>>>> {
58 self.items_by_properties().get(&properties_type).cloned()
59 }
60
61 fn get_items_by_category(
62 &self,
63 item_category: ItemCategory,
64 ) -> Option<Arc<Vec<Arc<ItemData>>>> {
65 self.items_by_category().get(&item_category).cloned()
66 }
67
68 fn get_item_ids_by_category(&self, item_category: ItemCategory) -> Option<Arc<Vec<i32>>> {
69 self.item_ids_by_category().get(&item_category).cloned()
70 }
71
72 fn is_item_in_category(&self, item_data: Arc<ItemData>, category: ItemCategory) -> bool {
73 self.is_item_id_in_category(item_data.id, category)
74 }
75
76 fn is_item_id_in_category(&self, item_id: i32, item_category: ItemCategory) -> bool {
77 self.get_item_ids_by_category(item_category)
78 .unwrap_or_default()
79 .contains(&item_id)
80 }
81}
82
83#[derive(Debug, Default, PartialEq)]
84pub struct Config {
85 species: Arc<HashMap<i32, Arc<SpeciesData>>>,
87 locations: Arc<HashMap<i32, Arc<LocationData>>>,
88 items: Arc<HashMap<i32, Arc<ItemData>>>,
89 settings: Arc<Settings>,
90 species_names: Arc<HashMap<i32, String>>,
91 location_names: Arc<HashMap<i32, String>>,
92 item_names: Arc<HashMap<i32, String>>,
93 items_by_attributes: Arc<HashMap<ItemAttributesType, Arc<Vec<Arc<ItemData>>>>>,
94 items_by_properties: Arc<HashMap<ItemPropertiesType, Arc<Vec<Arc<ItemData>>>>>,
95 items_by_category: Arc<HashMap<ItemCategory, Arc<Vec<Arc<ItemData>>>>>,
96 item_ids_by_category: Arc<HashMap<ItemCategory, Arc<Vec<i32>>>>,
97}
98
99impl ConfigInterface for Config {
100 fn species(&self) -> Arc<HashMap<i32, Arc<SpeciesData>>> {
101 self.species.clone()
102 }
103
104 fn locations(&self) -> Arc<HashMap<i32, Arc<LocationData>>> {
105 self.locations.clone()
106 }
107
108 fn items(&self) -> Arc<HashMap<i32, Arc<ItemData>>> {
109 self.items.clone()
110 }
111
112 fn settings(&self) -> Arc<Settings> {
113 self.settings.clone()
114 }
115
116 fn species_names(&self) -> Arc<HashMap<i32, String>> {
117 self.species_names.clone()
118 }
119
120 fn location_names(&self) -> Arc<HashMap<i32, String>> {
121 self.location_names.clone()
122 }
123
124 fn item_names(&self) -> Arc<HashMap<i32, String>> {
125 self.item_names.clone()
126 }
127
128 fn items_by_attributes(&self) -> Arc<HashMap<ItemAttributesType, Arc<Vec<Arc<ItemData>>>>> {
129 self.items_by_attributes.clone()
130 }
131
132 fn items_by_properties(&self) -> Arc<HashMap<ItemPropertiesType, Arc<Vec<Arc<ItemData>>>>> {
133 self.items_by_properties.clone()
134 }
135
136 fn items_by_category(&self) -> Arc<HashMap<ItemCategory, Arc<Vec<Arc<ItemData>>>>> {
137 self.items_by_category.clone()
138 }
139
140 fn item_ids_by_category(&self) -> Arc<HashMap<ItemCategory, Arc<Vec<i32>>>> {
141 self.item_ids_by_category.clone()
142 }
143}
144
145impl Config {
146 pub fn builder() -> ConfigBuilder {
147 ConfigBuilder::default()
148 }
149}
150
151pub trait ConfigBuilderInterface: Send + Sync {
152 fn build(self) -> Result<Arc<dyn ConfigInterface>, ConfigValidationReport>;
153}
154
155#[derive(Debug, Default)]
156pub struct ConfigBuilder {
157 config: Config,
158}
159
160impl ConfigBuilderInterface for ConfigBuilder {
161 fn build(self) -> Result<Arc<dyn ConfigInterface>, ConfigValidationReport> {
162 let validation_report = self.validate();
163 if validation_report.has_errors() {
164 Err(validation_report)
165 } else {
166 Ok(Arc::new(self.config))
167 }
168 }
169}
170
171impl ConfigBuilder {
172 pub fn new() -> Self {
173 Self::default()
174 }
175
176 pub fn species(mut self, specimens: HashMap<i32, SpeciesData>) -> Self {
177 let species = specimens
178 .into_iter()
179 .map(|(id, mut data)| {
180 data.id = id;
181 (id, Arc::new(data))
182 })
183 .collect();
184 self.config.species = Arc::new(species);
185
186 self.map_species_names();
187
188 self
189 }
190
191 pub fn species_json(self, json_string: &str) -> Result<Self, serde_json::Error> {
192 let specimens: HashMap<i32, SpeciesData> = serde_json::from_str(json_string)?;
193 Ok(self.species(specimens))
194 }
195
196 pub fn species_json_file(
197 self,
198 json_file_path: &Path,
199 ) -> Result<Self, Box<dyn std::error::Error>> {
200 let file = File::open(json_file_path)?;
201 let specimens: HashMap<i32, SpeciesData> = serde_json::from_reader(file)?;
202 Ok(self.species(specimens))
203 }
204
205 pub fn locations(mut self, locations: HashMap<i32, LocationData>) -> Self {
206 let locations = locations
207 .into_iter()
208 .map(|(id, mut data)| {
209 data.id = id;
210 (id, Arc::new(data))
211 })
212 .collect();
213 self.config.locations = Arc::new(locations);
214
215 self.map_location_names();
216
217 self
218 }
219
220 pub fn locations_json(self, json_string: &str) -> Result<Self, serde_json::Error> {
221 let locations: HashMap<i32, LocationData> = serde_json::from_str(json_string)?;
222 Ok(self.locations(locations))
223 }
224
225 pub fn locations_json_file(
226 self,
227 json_file_path: &Path,
228 ) -> Result<Self, Box<dyn std::error::Error>> {
229 let file = File::open(json_file_path)?;
230 let locations: HashMap<i32, LocationData> = serde_json::from_reader(file)?;
231 Ok(self.locations(locations))
232 }
233
234 pub fn items(mut self, items: HashMap<i32, ItemData>) -> Self {
235 let items = items
236 .into_iter()
237 .map(|(id, mut data)| {
238 data.id = id;
239 (id, Arc::new(data))
240 })
241 .collect();
242 self.config.items = Arc::new(items);
243
244 self.map_item_names();
245 self.map_items_by_attributes_and_properties_types();
246 self.map_items_by_category();
247
248 self
249 }
250
251 pub fn items_json(self, json_string: &str) -> Result<Self, serde_json::Error> {
252 let items: HashMap<i32, ItemData> = serde_json::from_str(json_string)?;
253 Ok(self.items(items))
254 }
255
256 pub fn items_json_file(
257 self,
258 json_file_path: &Path,
259 ) -> Result<Self, Box<dyn std::error::Error>> {
260 let file = File::open(json_file_path)?;
261 let items: HashMap<i32, ItemData> = serde_json::from_reader(file)?;
262 Ok(self.items(items))
263 }
264
265 pub fn settings(mut self, settings: Settings) -> Self {
266 self.config.settings = Arc::new(settings);
267 self
268 }
269
270 pub fn settings_json(self, json_string: &str) -> Result<Self, serde_json::Error> {
271 let settings: Settings = serde_json::from_str(json_string)?;
272 Ok(self.settings(settings))
273 }
274
275 pub fn settings_json_file(
276 self,
277 json_file_path: &Path,
278 ) -> Result<Self, Box<dyn std::error::Error>> {
279 let file = File::open(json_file_path)?;
280 let settings: Settings = serde_json::from_reader(file)?;
281 Ok(self.settings(settings))
282 }
283
284 fn map_species_names(&mut self) {
286 let species_names: HashMap<i32, String> = self
287 .config
288 .species
289 .iter()
290 .map(|(id, data)| (*id, data.name.clone()))
291 .collect();
292 self.config.species_names = Arc::new(species_names);
293 }
294
295 fn map_location_names(&mut self) {
296 let location_names: HashMap<i32, String> = self
297 .config
298 .locations
299 .iter()
300 .map(|(id, data)| (*id, data.name.clone()))
301 .collect();
302 self.config.location_names = Arc::new(location_names);
303 }
304
305 fn map_item_names(&mut self) {
306 let item_names: HashMap<i32, String> = self
307 .config
308 .items
309 .iter()
310 .map(|(id, data)| (*id, data.name.clone()))
311 .collect();
312 self.config.item_names = Arc::new(item_names);
313 }
314
315 fn map_items_by_attributes_and_properties_types(&mut self) {
316 let mut by_attributes: HashMap<ItemAttributesType, Vec<Arc<ItemData>>> = HashMap::new();
317 let mut by_properties: HashMap<ItemPropertiesType, Vec<Arc<ItemData>>> = HashMap::new();
318
319 self.config.items.values().for_each(|item| {
320 item.attributes
321 .get_attributes_types()
322 .iter()
323 .for_each(|attr_type| {
324 by_attributes
325 .entry(*attr_type)
326 .or_default()
327 .push(item.clone());
328 });
329
330 item.default_properties
331 .get_properties_types()
332 .iter()
333 .for_each(|prop_type| {
334 by_properties
335 .entry(*prop_type)
336 .or_default()
337 .push(item.clone());
338 });
339 });
340
341 let by_attributes = by_attributes
342 .into_iter()
343 .map(|(k, v)| (k, Arc::new(v)))
344 .collect();
345 let by_properties = by_properties
346 .into_iter()
347 .map(|(k, v)| (k, Arc::new(v)))
348 .collect();
349
350 self.config.items_by_attributes = Arc::new(by_attributes);
351 self.config.items_by_properties = Arc::new(by_properties);
352 }
353
354 pub fn map_items_by_category(&mut self) {
355 let mut by_category: HashMap<ItemCategory, Vec<Arc<ItemData>>> = HashMap::new();
356 let mut ids_by_category: HashMap<ItemCategory, Vec<i32>> = HashMap::new();
357
358 self.config.items.values().for_each(|item| {
359 if item.is_purchasable() {
360 by_category
361 .entry(ItemCategory::Shop)
362 .or_default()
363 .push(item.clone());
364 ids_by_category
365 .entry(ItemCategory::Shop)
366 .or_default()
367 .push(item.id);
368 }
369
370 if item.is_bait() {
371 by_category
372 .entry(ItemCategory::Bait)
373 .or_default()
374 .push(item.clone());
375 ids_by_category
376 .entry(ItemCategory::Bait)
377 .or_default()
378 .push(item.id);
379 }
380
381 if item.is_rod() {
382 by_category
383 .entry(ItemCategory::Rod)
384 .or_default()
385 .push(item.clone());
386 ids_by_category
387 .entry(ItemCategory::Rod)
388 .or_default()
389 .push(item.id);
390 }
391 });
392
393 let by_category = by_category
394 .into_iter()
395 .map(|(k, v)| (k, Arc::new(v)))
396 .collect();
397 let ids_by_category = ids_by_category
398 .into_iter()
399 .map(|(k, v)| (k, Arc::new(v)))
400 .collect();
401 self.config.items_by_category = Arc::new(by_category);
402 self.config.item_ids_by_category = Arc::new(ids_by_category);
403 }
404
405 pub fn validate(&self) -> ConfigValidationReport {
407 let mut report = ConfigValidationReport::new();
408 self.validate_species(&mut report);
409 self.validate_locations(&mut report);
410 self.validate_items(&mut report);
411 report
412 }
413
414 fn validate_locations(&self, report: &mut ConfigValidationReport) {
415 for location_data in self.config.locations.values() {
416 for required_location_id in &location_data.required_locations_unlocked {
417 self.validate_locations_required_locations(
418 report,
419 location_data,
420 *required_location_id,
421 );
422 }
423
424 for required_species_id in &location_data.required_species_caught {
425 self.validate_locations_required_species(
426 report,
427 location_data,
428 *required_species_id,
429 )
430 }
431 }
432 }
433
434 fn validate_locations_required_locations(
435 &self,
436 report: &mut ConfigValidationReport,
437 location_data: &Arc<LocationData>,
438 required_location_id: i32,
439 ) {
440 if self
441 .config
442 .get_location_data(required_location_id)
443 .is_none()
444 {
445 report.add_error(ConfigValidationError::location_required_location(
446 location_data.id,
447 required_location_id,
448 ));
449 }
450 }
451
452 fn validate_locations_required_species(
453 &self,
454 report: &mut ConfigValidationReport,
455 location_data: &Arc<LocationData>,
456 required_species_id: i32,
457 ) {
458 if self.config.get_species_data(required_species_id).is_none() {
459 report.add_error(ConfigValidationError::location_required_species(
460 location_data.id,
461 required_species_id,
462 ));
463 }
464 }
465
466 fn validate_species(&self, report: &mut ConfigValidationReport) {
467 for species_data in self.config.species.values() {
468 for encounter in &species_data.encounters {
469 self.validate_species_encounters(report, species_data, encounter);
470 }
471 }
472 }
473
474 fn validate_species_encounters(
475 &self,
476 report: &mut ConfigValidationReport,
477 species_data: &Arc<SpeciesData>,
478 encounter_data: &EncounterData,
479 ) {
480 let location_id = encounter_data.location_id;
481 if self.config.get_location_data(location_id).is_none() {
482 report.add_error(ConfigValidationError::species_encounter_location(
483 species_data.id,
484 location_id,
485 ));
486 }
487 }
488
489 fn validate_items(&self, report: &mut ConfigValidationReport) {
490 for item_data in self.config.items.values() {
491 if item_data.max_count < 1 {
492 report.add_error(ConfigValidationError::item_invalid_max_count(item_data.id));
493 }
494
495 if item_data.is_stackable() && item_data.max_count != 1 {
496 report.add_error(ConfigValidationError::item_non_unique_not_stackable(
497 item_data.id,
498 ));
499 }
500 }
501 }
502}