use anyhow::Result; use std::fmt; use std::fmt::{Debug, Formatter}; use hashbrown::HashMap; use crate::defines::LevelInt; use crate::static_data::GrowthRate; use crate::{StringKey, ValueIdentifiable, ValueIdentifier}; /// A library to store all growth rates. pub trait GrowthRateLibrary: Debug + ValueIdentifiable { /// Calculates the level for a given growth key name and a certain experience. fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result; /// Calculates the experience for a given growth key name and a certain level. fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result; /// Adds a new growth rate with a name and value. fn add_growth_rate(&mut self, key: &StringKey, value: Box); } /// A library to store all growth rates. pub struct GrowthRateLibraryImpl { /// A unique identifier so we know what value this is. identifier: ValueIdentifier, /// The underlying data structure. growth_rates: HashMap>, } impl GrowthRateLibraryImpl { /// Instantiates a new growth rate library with a capacity. pub fn new(capacity: usize) -> Self { Self { identifier: Default::default(), growth_rates: HashMap::with_capacity(capacity), } } } impl GrowthRateLibrary for GrowthRateLibraryImpl { /// Calculates the level for a given growth key name and a certain experience. fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result { Ok(self .growth_rates .get(growth_rate) .ok_or_else(|| anyhow::anyhow!("No growth rate found with key {}", growth_rate.to_string()))? .calculate_level(experience)) } /// Calculates the experience for a given growth key name and a certain level. fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result { self.growth_rates .get(growth_rate) .ok_or_else(|| anyhow::anyhow!("No growth rate found with key {}", growth_rate.to_string()))? .calculate_experience(level) } /// Adds a new growth rate with a name and value. fn add_growth_rate(&mut self, key: &StringKey, value: Box) { self.growth_rates.insert(key.clone(), value); } } impl ValueIdentifiable for GrowthRateLibraryImpl { fn value_identifier(&self) -> ValueIdentifier { self.identifier } } impl Debug for GrowthRateLibraryImpl { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("GrowthRateLibrary").finish() } } #[cfg(test)] #[allow(clippy::unwrap_used)] #[allow(clippy::indexing_slicing)] pub mod tests { use super::*; use crate::static_data::growth_rates::LookupGrowthRate; use crate::static_data::libraries::growth_rate_library::GrowthRateLibrary; use crate::static_data::GrowthRateLibraryImpl; pub fn build() -> GrowthRateLibraryImpl { let mut lib = GrowthRateLibraryImpl::new(1); // Borrow as mut so we can insert let w = &mut lib; w.add_growth_rate( &"test_growthrate".into(), Box::new(LookupGrowthRate::new(vec![0, 5, 10, 100])), ); // Drops borrow as mut lib } mockall::mock! { #[derive(Debug)] pub GrowthRateLibrary{} impl GrowthRateLibrary for GrowthRateLibrary { fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result; fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result; fn add_growth_rate(&mut self, key: &StringKey, value: Box); } impl ValueIdentifiable for GrowthRateLibrary { fn value_identifier(&self) -> ValueIdentifier{ ValueIdentifier::new(0) } } } #[test] fn add_growth_rate_to_library_and_calculate_level() { let lib = build(); assert_eq!(lib.calculate_level(&"test_growthrate".into(), 3).unwrap(), 1); assert_eq!(lib.calculate_level(&"test_growthrate".into(), 50).unwrap(), 3); } #[test] fn add_growth_rate_to_library_and_calculate_experience() { let lib = build(); assert_eq!(lib.calculate_experience(&"test_growthrate".into(), 1).unwrap(), 0); assert_eq!(lib.calculate_experience(&"test_growthrate".into(), 3).unwrap(), 10); } }