use crate::static_data::Nature; use crate::{Random, StringKey, ValueIdentifiable, ValueIdentifier}; use anyhow::bail; use anyhow_ext::{ensure, Result}; use indexmap::IndexMap; use std::fmt::Debug; use std::sync::Arc; /// A library of all natures that can be used, stored by their names. pub trait NatureLibrary: Debug + ValueIdentifiable { /// Adds a new nature with name to the library. fn load_nature(&mut self, name: StringKey, nature: Arc); /// Gets a nature by name. fn get_nature(&self, key: &StringKey) -> Option>; /// Gets a random nature. fn get_random_nature(&self, rand: &mut Random) -> Result>; /// Finds a nature name by nature. fn get_nature_name(&self, nature: &Arc) -> Result; } /// A library of all natures that can be used, stored by their names. #[derive(Debug)] pub struct NatureLibraryImpl { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, /// The underlying data structure. map: IndexMap>, } impl NatureLibraryImpl { /// Creates a new nature library with a given capacity. pub fn new(capacity: usize) -> Self { Self { identifier: Default::default(), map: IndexMap::with_capacity(capacity), } } } impl NatureLibrary for NatureLibraryImpl { /// Adds a new nature with name to the library. fn load_nature(&mut self, name: StringKey, nature: Arc) { self.map.insert(name, nature); } /// Gets a nature by name. fn get_nature(&self, key: &StringKey) -> Option> { self.map.get(key).cloned() } fn get_random_nature(&self, rand: &mut Random) -> Result> { ensure!(!self.map.is_empty(), "No natures were loaded into the library."); let i = rand.get_between(0, self.map.len() as i32); Ok(self .map .get_index(i as usize) // this should never happen, but we'll check anyway. .ok_or(anyhow_ext::anyhow!("Failed to get a random nature from the library."))? .1 .clone()) } /// Finds a nature name by nature. fn get_nature_name(&self, nature: &Arc) -> Result { for kv in &self.map { // As natures can't be copied, and should always be the same reference as the value // in the map, we just compare by reference. if kv.1.value_identifier() == nature.value_identifier() { return Ok(kv.0.clone()); } } bail!("No name was found for the given nature. The nature may not be in the library."); } } impl ValueIdentifiable for NatureLibraryImpl { fn value_identifier(&self) -> ValueIdentifier { self.identifier } } #[cfg(test)] #[allow(clippy::indexing_slicing)] #[allow(clippy::unwrap_used)] #[allow(clippy::expect_used)] pub mod tests { use super::*; use crate::static_data::statistics::Statistic; use crate::static_data::{NatureImpl, NatureLibrary, NatureLibraryImpl}; pub fn build() -> NatureLibraryImpl { let mut lib = NatureLibraryImpl::new(2); lib.load_nature( "test_nature".into(), NatureImpl::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), ); lib } mockall::mock! { #[derive(Debug)] pub NatureLibrary{} impl NatureLibrary for NatureLibrary { fn load_nature(&mut self, name: StringKey, nature: Arc); fn get_nature(&self, key: &StringKey) -> Option>; fn get_nature_name(&self, nature: &Arc) -> Result; fn get_random_nature(&self, rand: &mut Random) -> Result>; } impl ValueIdentifiable for NatureLibrary { fn value_identifier(&self) -> ValueIdentifier{ ValueIdentifier::new(0) } } } #[test] fn create_nature_library_insert_and_retrieve() { let mut lib = NatureLibraryImpl::new(2); lib.load_nature( "foo".into(), NatureImpl::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), ); lib.load_nature( "bar".into(), NatureImpl::new(Statistic::Attack, Statistic::Defense, 1.1, 0.9), ); let n1 = lib.get_nature(&"foo".into()).expect("Nature was not found"); assert_eq!(n1.increased_stat(), Statistic::HP); assert_eq!(n1.decreased_stat(), Statistic::Attack); assert_eq!(n1.get_stat_modifier(n1.increased_stat()), 1.1); assert_eq!(n1.get_stat_modifier(n1.decreased_stat()), 0.9); } #[test] fn create_nature_library_insert_and_get_name() { let mut lib = NatureLibraryImpl::new(2); lib.load_nature( "foo".into(), NatureImpl::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), ); lib.load_nature( "bar".into(), NatureImpl::new(Statistic::Attack, Statistic::Defense, 1.1, 0.9), ); let n1 = lib.get_nature(&"foo".into()).expect("Nature was not found"); let name = lib.get_nature_name(&n1).unwrap(); assert_eq!(name, "foo".into()); let n2 = lib.get_nature(&"bar".into()).expect("Nature was not found"); let name2 = lib.get_nature_name(&n2).unwrap(); assert_eq!(name2, "bar".into()); } #[test] fn create_nature_library_insert_single_and_retrieve_random() { let mut lib = NatureLibraryImpl::new(2); lib.load_nature( "foo".into(), NatureImpl::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), ); let n1 = lib.get_random_nature(&mut Random::new(0)).unwrap(); assert_eq!(n1.increased_stat(), Statistic::HP); assert_eq!(n1.decreased_stat(), Statistic::Attack); assert_eq!(n1.get_stat_modifier(n1.increased_stat()), 1.1); assert_eq!(n1.get_stat_modifier(n1.decreased_stat()), 0.9); } }