use anyhow::Result; use std::fmt; use std::fmt::{Debug, Formatter}; use std::sync::Arc; use hashbrown::HashMap; use parking_lot::RwLock; use crate::defines::LevelInt; use crate::static_data::GrowthRate; use crate::StringKey; /// A library to store all growth rates. pub trait GrowthRateLibrary: Debug { /// 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(&self, key: &StringKey, value: Arc); } /// A library to store all growth rates. pub struct GrowthRateLibraryImpl { /// The underlying data structure. growth_rates: RwLock>>, } impl GrowthRateLibraryImpl { /// Instantiates a new growth rate library with a capacity. pub fn new(capacity: usize) -> Self { Self { growth_rates: RwLock::new(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 .read() .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 .read() .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(&self, key: &StringKey, value: Arc) { self.growth_rates.write().insert(key.clone(), value); } } 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; 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(), Arc::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(&self, key: &StringKey, value: Arc); } } #[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); } }