fish_lib/
config.rs

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    /// Mapping specimen to their species ID
86    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    // Helper function for building config indices
285    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    // Validation
406    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}