Replace most panics in the core library with results
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Deukhoofd 2023-04-19 18:44:11 +02:00
parent 00d596d656
commit c0e4702e45
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
33 changed files with 222 additions and 102 deletions

View File

@ -160,6 +160,7 @@ impl ValueIdentifiable for ChoiceQueue {
#[cfg(test)] #[cfg(test)]
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)]
#[allow(clippy::indexing_slicing)]
mod tests { mod tests {
use super::*; use super::*;
use crate::defines::LevelInt; use crate::defines::LevelInt;

View File

@ -213,7 +213,7 @@ impl Battle {
.library() .library()
.static_data() .static_data()
.types() .types()
.get_effectiveness(hit_type, &target.types()); .get_effectiveness(hit_type, &target.types())?;
script_hook!( script_hook!(
change_effectiveness, change_effectiveness,
executing_move, executing_move,

View File

@ -147,6 +147,7 @@ impl ValueIdentifiable for Gen7BattleStatCalculator {
} }
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
pub mod tests { pub mod tests {
use super::*; use super::*;

View File

@ -127,6 +127,7 @@ impl ValueIdentifiable for DynamicLibraryImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
pub mod test { pub mod test {
use super::*; use super::*;
use crate::dynamic_data::libraries::battle_stat_calculator::Gen7BattleStatCalculator; use crate::dynamic_data::libraries::battle_stat_calculator::Gen7BattleStatCalculator;

View File

@ -101,6 +101,7 @@ impl ValueIdentifiable for LearnedMove {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests { mod tests {
use super::*; use super::*;
use crate::static_data::tests::MockMoveData; use crate::static_data::tests::MockMoveData;

View File

@ -421,7 +421,7 @@ impl Pokemon {
} }
let form = self.form(); let form = self.form();
let ability = form.get_ability(self.ability_index); let ability = form.get_ability(self.ability_index)?;
Ok(self Ok(self
.library .library
.static_data() .static_data()

View File

@ -53,7 +53,7 @@ impl PokemonBuilder {
.species() .species()
.get(&self.species) .get(&self.species)
.ok_or(PkmnError::InvalidSpeciesName { species: self.species })?; .ok_or(PkmnError::InvalidSpeciesName { species: self.species })?;
let form = species.get_default_form(); let form = species.get_default_form()?;
let p = Pokemon::new( let p = Pokemon::new(
self.library, self.library,
species, species,

View File

@ -388,6 +388,8 @@ impl Clone for ScriptContainer {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::unwrap_used)]
#[allow(clippy::indexing_slicing)]
mod tests { mod tests {
use std::sync::atomic::{AtomicBool, AtomicPtr}; use std::sync::atomic::{AtomicBool, AtomicPtr};

View File

@ -1,5 +1,5 @@
use crate::defines::LevelInt; use crate::defines::LevelInt;
use crate::ffi::{BorrowedPtr, ExternPointer, OwnedPtr}; use crate::ffi::{BorrowedPtr, ExternPointer, NativeResult, OwnedPtr};
use crate::static_data::{LearnableMoves, LearnableMovesImpl}; use crate::static_data::{LearnableMoves, LearnableMovesImpl};
use std::ffi::{c_char, CStr}; use std::ffi::{c_char, CStr};
use std::ptr::drop_in_place; use std::ptr::drop_in_place;
@ -22,6 +22,8 @@ unsafe extern "C" fn learnable_moves_add_level_move(
mut ptr: ExternPointer<Box<dyn LearnableMoves>>, mut ptr: ExternPointer<Box<dyn LearnableMoves>>,
level: LevelInt, level: LevelInt,
move_name: BorrowedPtr<c_char>, move_name: BorrowedPtr<c_char>,
) { ) -> NativeResult<()> {
ptr.as_mut().add_level_move(level, &CStr::from_ptr(move_name).into()) ptr.as_mut()
.add_level_move(level, &CStr::from_ptr(move_name).into())
.into()
} }

View File

@ -23,9 +23,10 @@ unsafe extern "C" fn growth_rate_library_calculate_level(
ptr: ExternPointer<Box<dyn GrowthRateLibrary>>, ptr: ExternPointer<Box<dyn GrowthRateLibrary>>,
growth_rate: BorrowedPtr<c_char>, growth_rate: BorrowedPtr<c_char>,
experience: u32, experience: u32,
) -> LevelInt { ) -> NativeResult<LevelInt> {
ptr.as_ref() ptr.as_ref()
.calculate_level(&CStr::from_ptr(growth_rate).into(), experience) .calculate_level(&CStr::from_ptr(growth_rate).into(), experience)
.into()
} }
/// Calculates the experience for a given growth key name and a certain level. /// Calculates the experience for a given growth key name and a certain level.

View File

@ -54,9 +54,12 @@ unsafe extern "C" fn nature_library_get_nature(
unsafe extern "C" fn nature_library_get_random_nature( unsafe extern "C" fn nature_library_get_random_nature(
ptr: ExternPointer<Box<dyn NatureLibrary>>, ptr: ExternPointer<Box<dyn NatureLibrary>>,
seed: u64, seed: u64,
) -> IdentifiablePointer<Arc<dyn Nature>> { ) -> NativeResult<IdentifiablePointer<Arc<dyn Nature>>> {
let mut rand = Random::new(seed as u128); let mut rand = Random::new(seed as u128);
ptr.as_ref().get_random_nature(&mut rand).into() match ptr.as_ref().get_random_nature(&mut rand) {
Ok(nature) => NativeResult::ok(nature.into()),
Err(e) => NativeResult::err(e),
}
} }
/// Finds a nature name by nature. /// Finds a nature name by nature.
@ -68,9 +71,12 @@ unsafe extern "C" fn nature_library_get_nature_name(
match nature.as_ref() { match nature.as_ref() {
Some(nature) => { Some(nature) => {
let name = ptr.as_ref().get_nature_name(nature); let name = ptr.as_ref().get_nature_name(nature);
match CString::new(name.str()) { match name {
Ok(name) => match CString::new(name.str()) {
Ok(cstr) => NativeResult::ok(cstr.into_raw()), Ok(cstr) => NativeResult::ok(cstr.into_raw()),
Err(_) => NativeResult::err(anyhow!("Failed to convert nature name to C string")), Err(_) => NativeResult::err(anyhow!("Failed to convert nature name to C string")),
},
Err(e) => NativeResult::err(e),
} }
} }
None => NativeResult::err(PkmnError::NullReference.into()), None => NativeResult::err(PkmnError::NullReference.into()),

View File

@ -58,8 +58,8 @@ extern "C" fn type_library_get_single_effectiveness(
ptr: ExternPointer<Box<dyn TypeLibrary>>, ptr: ExternPointer<Box<dyn TypeLibrary>>,
attacking: TypeIdentifier, attacking: TypeIdentifier,
defending: TypeIdentifier, defending: TypeIdentifier,
) -> f32 { ) -> NativeResult<f32> {
ptr.as_ref().get_single_effectiveness(attacking, defending) ptr.as_ref().get_single_effectiveness(attacking, defending).into()
} }
/// Gets the effectiveness for a single attacking type against an amount of defending types. /// Gets the effectiveness for a single attacking type against an amount of defending types.
@ -71,9 +71,9 @@ unsafe extern "C" fn type_library_get_effectiveness(
attacking: TypeIdentifier, attacking: TypeIdentifier,
defending: OwnedPtr<TypeIdentifier>, defending: OwnedPtr<TypeIdentifier>,
defending_length: usize, defending_length: usize,
) -> f32 { ) -> NativeResult<f32> {
let v = std::slice::from_raw_parts(defending, defending_length); let v = std::slice::from_raw_parts(defending, defending_length);
ptr.as_ref().get_effectiveness(attacking, v) ptr.as_ref().get_effectiveness(attacking, v).into()
} }
/// Registers a new type in the library. /// Registers a new type in the library.
@ -92,6 +92,8 @@ unsafe extern "C" fn type_library_set_effectiveness(
attacking: TypeIdentifier, attacking: TypeIdentifier,
defending: TypeIdentifier, defending: TypeIdentifier,
effectiveness: f32, effectiveness: f32,
) { ) -> NativeResult<()> {
ptr.as_mut().set_effectiveness(attacking, defending, effectiveness); ptr.as_mut()
.set_effectiveness(attacking, defending, effectiveness)
.into()
} }

View File

@ -8,12 +8,12 @@
#![deny(clippy::missing_docs_in_private_items)] #![deny(clippy::missing_docs_in_private_items)]
// Linter rules to prevent panics // Linter rules to prevent panics
// Currently still a WIP to fix all of these // Currently still a WIP to fix all of these
// #![deny(clippy::unwrap_used)] #![deny(clippy::unwrap_used)]
// #![deny(clippy::expect_used)] #![deny(clippy::expect_used)]
// #![deny(clippy::indexing_slicing)] #![deny(clippy::indexing_slicing)]
// #![deny(clippy::string_slice)] #![deny(clippy::string_slice)]
// #![deny(clippy::exit)] #![deny(clippy::exit)]
// #![deny(clippy::panic)] #![deny(clippy::panic)]
// Features // Features
#![feature(test)] #![feature(test)]
#![feature(const_option)] #![feature(const_option)]
@ -58,6 +58,7 @@ pub mod static_data;
/// The utils module includes misc utils that are used within PkmnLib /// The utils module includes misc utils that are used within PkmnLib
pub mod utils; pub mod utils;
use crate::static_data::TypeIdentifier;
use thiserror::Error; use thiserror::Error;
/// The PkmnError enum is the error type for PkmnLib. It is used to return common errors from functions /// The PkmnError enum is the error type for PkmnLib. It is used to return common errors from functions
@ -103,6 +104,12 @@ pub enum PkmnError {
/// The species that was requested /// The species that was requested
species: StringKey, species: StringKey,
}, },
/// Unable to get type
#[error("Unable to get type {type_id}")]
InvalidTypeIdentifier {
/// The type that was requested
type_id: TypeIdentifier,
},
/// The given pointer is not a valid CString /// The given pointer is not a valid CString
#[error("The given pointer is not a valid CString")] #[error("The given pointer is not a valid CString")]
InvalidCString, InvalidCString,

View File

@ -29,7 +29,7 @@ register! {
defending: u8 defending: u8
) -> f32 { ) -> f32 {
let lib = lib.value_func_arc(&env).unwrap(); let lib = lib.value_func_arc(&env).unwrap();
lib.get_single_effectiveness(attacking.into(), defending.into()) lib.get_single_effectiveness(attacking.into(), defending.into()).unwrap()
} }
} }

View File

@ -49,6 +49,7 @@ impl GrowthRate for LookupGrowthRate {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;

View File

@ -135,6 +135,7 @@ impl ValueIdentifiable for ItemImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;

View File

@ -47,6 +47,8 @@ impl ValueIdentifiable for AbilityLibraryImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub mod tests { pub mod tests {
use crate::static_data::libraries::ability_library::AbilityLibraryImpl; use crate::static_data::libraries::ability_library::AbilityLibraryImpl;
use crate::static_data::AbilityImpl; use crate::static_data::AbilityImpl;

View File

@ -11,7 +11,7 @@ use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
/// A library to store all growth rates. /// A library to store all growth rates.
pub trait GrowthRateLibrary: Debug + ValueIdentifiable { pub trait GrowthRateLibrary: Debug + ValueIdentifiable {
/// Calculates the level for a given growth key name and a certain experience. /// Calculates the level for a given growth key name and a certain experience.
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> LevelInt; fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result<LevelInt>;
/// Calculates the experience for a given growth key name and a certain level. /// Calculates the experience for a given growth key name and a certain level.
fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32>; fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32>;
/// Adds a new growth rate with a name and value. /// Adds a new growth rate with a name and value.
@ -38,12 +38,19 @@ impl GrowthRateLibraryImpl {
impl GrowthRateLibrary for GrowthRateLibraryImpl { impl GrowthRateLibrary for GrowthRateLibraryImpl {
/// Calculates the level for a given growth key name and a certain experience. /// Calculates the level for a given growth key name and a certain experience.
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> LevelInt { fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result<LevelInt> {
self.growth_rates[growth_rate].calculate_level(experience) 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. /// Calculates the experience for a given growth key name and a certain level.
fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32> { fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32> {
self.growth_rates[growth_rate].calculate_experience(level) 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. /// Adds a new growth rate with a name and value.
@ -65,6 +72,8 @@ impl Debug for GrowthRateLibraryImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::unwrap_used)]
#[allow(clippy::indexing_slicing)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::static_data::growth_rates::LookupGrowthRate; use crate::static_data::growth_rates::LookupGrowthRate;
@ -89,7 +98,7 @@ pub mod tests {
#[derive(Debug)] #[derive(Debug)]
pub GrowthRateLibrary{} pub GrowthRateLibrary{}
impl GrowthRateLibrary for GrowthRateLibrary { impl GrowthRateLibrary for GrowthRateLibrary {
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> LevelInt; fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result<LevelInt>;
fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32>; fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32>;
fn add_growth_rate(&mut self, key: &StringKey, value: Box<dyn GrowthRate>); fn add_growth_rate(&mut self, key: &StringKey, value: Box<dyn GrowthRate>);
} }
@ -103,8 +112,8 @@ pub mod tests {
#[test] #[test]
fn add_growth_rate_to_library_and_calculate_level() { fn add_growth_rate_to_library_and_calculate_level() {
let lib = build(); let lib = build();
assert_eq!(lib.calculate_level(&"test_growthrate".into(), 3), 1); assert_eq!(lib.calculate_level(&"test_growthrate".into(), 3).unwrap(), 1);
assert_eq!(lib.calculate_level(&"test_growthrate".into(), 50), 3); assert_eq!(lib.calculate_level(&"test_growthrate".into(), 50).unwrap(), 3);
} }
#[test] #[test]

View File

@ -1,5 +1,7 @@
use crate::static_data::Nature; use crate::static_data::Nature;
use crate::{Random, StringKey, ValueIdentifiable, ValueIdentifier}; use crate::{Random, StringKey, ValueIdentifiable, ValueIdentifier};
use anyhow::bail;
use anyhow_ext::{ensure, Result};
use indexmap::IndexMap; use indexmap::IndexMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
@ -11,9 +13,9 @@ pub trait NatureLibrary: Debug + ValueIdentifiable {
/// Gets a nature by name. /// Gets a nature by name.
fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>>; fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>>;
/// Gets a random nature. /// Gets a random nature.
fn get_random_nature(&self, rand: &mut Random) -> Arc<dyn Nature>; fn get_random_nature(&self, rand: &mut Random) -> Result<Arc<dyn Nature>>;
/// Finds a nature name by nature. /// Finds a nature name by nature.
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> StringKey; fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> Result<StringKey>;
} }
/// A library of all natures that can be used, stored by their names. /// A library of all natures that can be used, stored by their names.
@ -46,21 +48,28 @@ impl NatureLibrary for NatureLibraryImpl {
self.map.get(key).cloned() self.map.get(key).cloned()
} }
fn get_random_nature(&self, rand: &mut Random) -> Arc<dyn Nature> { fn get_random_nature(&self, rand: &mut Random) -> Result<Arc<dyn Nature>> {
ensure!(!self.map.is_empty(), "No natures were loaded into the library.");
let i = rand.get_between(0, self.map.len() as i32); let i = rand.get_between(0, self.map.len() as i32);
return self.map.get_index(i as usize).unwrap().1.clone(); 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. /// Finds a nature name by nature.
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> StringKey { fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> Result<StringKey> {
for kv in &self.map { for kv in &self.map {
// As natures can't be copied, and should always be the same reference as the value // As natures can't be copied, and should always be the same reference as the value
// in the map, we just compare by reference. // in the map, we just compare by reference.
if kv.1.value_identifier() == nature.value_identifier() { if kv.1.value_identifier() == nature.value_identifier() {
return kv.0.clone(); return Ok(kv.0.clone());
} }
} }
panic!("No name was found for the given nature. This should never happen."); bail!("No name was found for the given nature. The nature may not be in the library.");
} }
} }
@ -71,6 +80,9 @@ impl ValueIdentifiable for NatureLibraryImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
#[allow(clippy::expect_used)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::static_data::statistics::Statistic; use crate::static_data::statistics::Statistic;
@ -93,8 +105,8 @@ pub mod tests {
impl NatureLibrary for NatureLibrary { impl NatureLibrary for NatureLibrary {
fn load_nature(&mut self, name: StringKey, nature: Arc<dyn Nature>); fn load_nature(&mut self, name: StringKey, nature: Arc<dyn Nature>);
fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>>; fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>>;
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> StringKey; fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> Result<StringKey>;
fn get_random_nature(&self, rand: &mut Random) -> Arc<dyn Nature>; fn get_random_nature(&self, rand: &mut Random) -> Result<Arc<dyn Nature>>;
} }
impl ValueIdentifiable for NatureLibrary { impl ValueIdentifiable for NatureLibrary {
fn value_identifier(&self) -> ValueIdentifier{ fn value_identifier(&self) -> ValueIdentifier{
@ -134,10 +146,10 @@ pub mod tests {
); );
let n1 = lib.get_nature(&"foo".into()).expect("Nature was not found"); let n1 = lib.get_nature(&"foo".into()).expect("Nature was not found");
let name = lib.get_nature_name(&n1); let name = lib.get_nature_name(&n1).unwrap();
assert_eq!(name, "foo".into()); assert_eq!(name, "foo".into());
let n2 = lib.get_nature(&"bar".into()).expect("Nature was not found"); let n2 = lib.get_nature(&"bar".into()).expect("Nature was not found");
let name2 = lib.get_nature_name(&n2); let name2 = lib.get_nature_name(&n2).unwrap();
assert_eq!(name2, "bar".into()); assert_eq!(name2, "bar".into());
} }
@ -148,7 +160,7 @@ pub mod tests {
"foo".into(), "foo".into(),
NatureImpl::new(Statistic::HP, Statistic::Attack, 1.1, 0.9), NatureImpl::new(Statistic::HP, Statistic::Attack, 1.1, 0.9),
); );
let n1 = lib.get_random_nature(&mut Random::new(0)); let n1 = lib.get_random_nature(&mut Random::new(0)).unwrap();
assert_eq!(n1.increased_stat(), Statistic::HP); assert_eq!(n1.increased_stat(), Statistic::HP);
assert_eq!(n1.decreased_stat(), Statistic::Attack); 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.increased_stat()), 1.1);

View File

@ -47,6 +47,8 @@ impl ValueIdentifiable for SpeciesLibraryImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::static_data::{FormImpl, LearnableMovesImpl, SpeciesImpl, StaticStatisticSet}; use crate::static_data::{FormImpl, LearnableMovesImpl, SpeciesImpl, StaticStatisticSet};

View File

@ -164,6 +164,8 @@ impl ValueIdentifiable for StaticDataImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub mod test { pub mod test {
use super::*; use super::*;
use crate::static_data::LibrarySettingsImpl; use crate::static_data::LibrarySettingsImpl;

View File

@ -1,8 +1,9 @@
use anyhow_ext::Result;
use atomig::Atom; use atomig::Atom;
use hashbrown::HashMap; use hashbrown::HashMap;
use std::fmt::Debug; use std::fmt::{Debug, Display};
use crate::{StringKey, ValueIdentifiable, ValueIdentifier}; use crate::{PkmnError, StringKey, ValueIdentifiable, ValueIdentifier};
/// A unique key that can be used to store a reference to a type. Opaque reference to a byte /// A unique key that can be used to store a reference to a type. Opaque reference to a byte
/// internally. /// internally.
@ -25,6 +26,12 @@ impl From<TypeIdentifier> for u8 {
} }
} }
impl Display for TypeIdentifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TypeId({})", self.val)
}
}
/// All data related to types and effectiveness. /// All data related to types and effectiveness.
pub trait TypeLibrary: Debug + ValueIdentifiable { pub trait TypeLibrary: Debug + ValueIdentifiable {
/// Gets the type identifier for a type with a name. /// Gets the type identifier for a type with a name.
@ -34,18 +41,23 @@ pub trait TypeLibrary: Debug + ValueIdentifiable {
fn get_type_name(&self, t: TypeIdentifier) -> Option<StringKey>; fn get_type_name(&self, t: TypeIdentifier) -> Option<StringKey>;
/// Gets the effectiveness for a single attacking type against a single defending type. /// Gets the effectiveness for a single attacking type against a single defending type.
fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> f32; fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> Result<f32>;
/// Gets the effectiveness for a single attacking type against an amount of defending types. /// Gets the effectiveness for a single attacking type against an amount of defending types.
/// This is equivalent to running [`get_single_effectiveness`] on each defending type, and /// This is equivalent to running [`get_single_effectiveness`] on each defending type, and
/// multiplying the results with each other. /// multiplying the results with each other.
fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> f32; fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> Result<f32>;
/// Registers a new type in the library. /// Registers a new type in the library.
fn register_type(&mut self, name: &StringKey) -> TypeIdentifier; fn register_type(&mut self, name: &StringKey) -> TypeIdentifier;
/// Sets the effectiveness for an attacking type against a defending type. /// Sets the effectiveness for an attacking type against a defending type.
fn set_effectiveness(&mut self, attacking: TypeIdentifier, defending: TypeIdentifier, effectiveness: f32); fn set_effectiveness(
&mut self,
attacking: TypeIdentifier,
defending: TypeIdentifier,
effectiveness: f32,
) -> Result<()>;
} }
/// All data related to types and effectiveness. /// All data related to types and effectiveness.
@ -87,19 +99,24 @@ impl TypeLibrary for TypeLibraryImpl {
} }
/// Gets the effectiveness for a single attacking type against a single defending type. /// Gets the effectiveness for a single attacking type against a single defending type.
fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> f32 { fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> Result<f32> {
self.effectiveness[(attacking.val - 1) as usize][(defending.val - 1) as usize] Ok(*self
.effectiveness
.get((attacking.val - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: attacking })?
.get((defending.val - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: defending })?)
} }
/// Gets the effectiveness for a single attacking type against an amount of defending types. /// Gets the effectiveness for a single attacking type against an amount of defending types.
/// This is equivalent to running [`get_single_effectiveness`] on each defending type, and /// This is equivalent to running [`get_single_effectiveness`] on each defending type, and
/// multiplying the results with each other. /// multiplying the results with each other.
fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> f32 { fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> Result<f32> {
let mut e = 1.0; let mut e = 1.0;
for def in defending { for def in defending {
e *= self.get_single_effectiveness(attacking, *def); e *= self.get_single_effectiveness(attacking, *def)?;
} }
e Ok(e)
} }
/// Registers a new type in the library. /// Registers a new type in the library.
@ -116,8 +133,19 @@ impl TypeLibrary for TypeLibraryImpl {
} }
/// Sets the effectiveness for an attacking type against a defending type. /// Sets the effectiveness for an attacking type against a defending type.
fn set_effectiveness(&mut self, attacking: TypeIdentifier, defending: TypeIdentifier, effectiveness: f32) { fn set_effectiveness(
self.effectiveness[(attacking.val - 1) as usize][(defending.val - 1) as usize] = effectiveness; &mut self,
attacking: TypeIdentifier,
defending: TypeIdentifier,
effectiveness: f32,
) -> Result<()> {
*self
.effectiveness
.get_mut((attacking.val - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: attacking })?
.get_mut((defending.val - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: defending })? = effectiveness;
Ok(())
} }
} }
@ -128,6 +156,8 @@ impl ValueIdentifiable for TypeLibraryImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub mod tests { pub mod tests {
use assert_approx_eq::assert_approx_eq; use assert_approx_eq::assert_approx_eq;
@ -143,8 +173,8 @@ pub mod tests {
let t1 = w.register_type(&"bar".into()); let t1 = w.register_type(&"bar".into());
// Drops borrow as mut // Drops borrow as mut
w.set_effectiveness(t0, t1, 0.5); w.set_effectiveness(t0, t1, 0.5).unwrap();
w.set_effectiveness(t1, t0, 2.0); w.set_effectiveness(t1, t0, 2.0).unwrap();
lib lib
} }
@ -173,14 +203,14 @@ pub mod tests {
let w = &mut lib; let w = &mut lib;
let t0 = w.register_type(&"foo".into()); let t0 = w.register_type(&"foo".into());
let t1 = w.register_type(&"bar".into()); let t1 = w.register_type(&"bar".into());
w.set_effectiveness(t0, t1, 0.5); w.set_effectiveness(t0, t1, 0.5).unwrap();
w.set_effectiveness(t1, t0, 2.0); w.set_effectiveness(t1, t0, 2.0).unwrap();
// Drops borrow as mut // Drops borrow as mut
// Borrow as read so we can read // Borrow as read so we can read
let r = &lib; let r = &lib;
assert_approx_eq!(r.get_single_effectiveness(t0, t1), 0.5); assert_approx_eq!(r.get_single_effectiveness(t0, t1).unwrap(), 0.5);
assert_approx_eq!(r.get_single_effectiveness(t1, t0), 2.0); assert_approx_eq!(r.get_single_effectiveness(t1, t0).unwrap(), 2.0);
} }
#[test] #[test]
@ -191,14 +221,14 @@ pub mod tests {
let w = &mut lib; let w = &mut lib;
let t0 = w.register_type(&"foo".into()); let t0 = w.register_type(&"foo".into());
let t1 = w.register_type(&"bar".into()); let t1 = w.register_type(&"bar".into());
w.set_effectiveness(t0, t1, 0.5); w.set_effectiveness(t0, t1, 0.5).unwrap();
w.set_effectiveness(t1, t0, 2.0); w.set_effectiveness(t1, t0, 2.0).unwrap();
// Drops borrow as mut // Drops borrow as mut
// Borrow as read so we can read // Borrow as read so we can read
let r = &lib; let r = &lib;
assert_approx_eq!(r.get_effectiveness(t0, &[t1, t1]), 0.25); assert_approx_eq!(r.get_effectiveness(t0, &[t1, t1]).unwrap(), 0.25);
assert_approx_eq!(r.get_effectiveness(t1, &[t0, t0]), 4.0); assert_approx_eq!(r.get_effectiveness(t1, &[t0, t0]).unwrap(), 4.0);
} }
#[test] #[test]

View File

@ -208,6 +208,8 @@ impl ValueIdentifiable for MoveDataImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;

View File

@ -59,6 +59,8 @@ impl ValueIdentifiable for SecondaryEffectImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
use assert_approx_eq::assert_approx_eq; use assert_approx_eq::assert_approx_eq;

View File

@ -96,6 +96,8 @@ impl ValueIdentifiable for NatureImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;

View File

@ -70,6 +70,8 @@ pub struct AbilityIndex {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;

View File

@ -1,3 +1,4 @@
use anyhow_ext::{ensure, Result};
use hashbrown::HashSet; use hashbrown::HashSet;
use std::fmt::Debug; use std::fmt::Debug;
@ -5,8 +6,8 @@ use crate::static_data::Statistic;
use crate::static_data::TypeIdentifier; use crate::static_data::TypeIdentifier;
use crate::static_data::{Ability, StaticStatisticSet}; use crate::static_data::{Ability, StaticStatisticSet};
use crate::static_data::{AbilityIndex, LearnableMoves}; use crate::static_data::{AbilityIndex, LearnableMoves};
use crate::StringKey;
use crate::{Random, ValueIdentifiable, ValueIdentifier}; use crate::{Random, ValueIdentifiable, ValueIdentifier};
use crate::{StringKey, VecExt};
/// A form is a variant of a specific species. A species always has at least one form, but can have /// A form is a variant of a specific species. A species always has at least one form, but can have
/// many more. /// many more.
@ -34,7 +35,7 @@ pub trait Form: ValueIdentifiable + Debug {
fn flags(&self) -> &HashSet<StringKey>; fn flags(&self) -> &HashSet<StringKey>;
/// Get a type of the move at a certain index. /// Get a type of the move at a certain index.
fn get_type(&self, index: usize) -> TypeIdentifier; fn get_type(&self, index: usize) -> Result<TypeIdentifier>;
/// Gets a single base stat value. /// Gets a single base stat value.
fn get_base_stat(&self, stat: Statistic) -> u16; fn get_base_stat(&self, stat: Statistic) -> u16;
@ -42,12 +43,12 @@ pub trait Form: ValueIdentifiable + Debug {
fn find_ability_index(&self, ability: &dyn Ability) -> Option<AbilityIndex>; fn find_ability_index(&self, ability: &dyn Ability) -> Option<AbilityIndex>;
/// Gets an ability from the form. /// Gets an ability from the form.
fn get_ability(&self, index: AbilityIndex) -> &StringKey; fn get_ability(&self, index: AbilityIndex) -> Result<&StringKey>;
/// Gets a random ability from the form. /// Gets a random ability from the form.
fn get_random_ability(&self, rand: &mut Random) -> &StringKey; fn get_random_ability(&self, rand: &mut Random) -> Result<&StringKey>;
/// Gets a random hidden ability from the form. /// Gets a random hidden ability from the form.
fn get_random_hidden_ability(&self, rand: &mut Random) -> &StringKey; fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<&StringKey>;
/// Check if the form has a specific flag set. /// Check if the form has a specific flag set.
fn has_flag(&self, key: &StringKey) -> bool; fn has_flag(&self, key: &StringKey) -> bool;
@ -158,8 +159,8 @@ impl Form for FormImpl {
} }
/// Get a type of the move at a certain index. /// Get a type of the move at a certain index.
fn get_type(&self, index: usize) -> TypeIdentifier { fn get_type(&self, index: usize) -> Result<TypeIdentifier> {
self.types[index] Ok(*self.types.get_res(index)?)
} }
/// Gets a single base stat value. /// Gets a single base stat value.
@ -189,21 +190,25 @@ impl Form for FormImpl {
} }
/// Gets an ability from the form. /// Gets an ability from the form.
fn get_ability(&self, index: AbilityIndex) -> &StringKey { fn get_ability(&self, index: AbilityIndex) -> Result<&StringKey> {
if index.hidden { if index.hidden {
&self.hidden_abilities[index.index as usize] Ok(self.hidden_abilities.get_res(index.index as usize)?)
} else { } else {
&self.abilities[index.index as usize] Ok(self.abilities.get_res(index.index as usize)?)
} }
} }
/// Gets a random ability from the form. /// Gets a random ability from the form.
fn get_random_ability(&self, rand: &mut Random) -> &StringKey { fn get_random_ability(&self, rand: &mut Random) -> Result<&StringKey> {
&self.abilities[rand.get_between_unsigned(0, self.abilities.len() as u32) as usize] ensure!(!self.abilities.is_empty(), "No abilities on form");
self.abilities
.get_res(rand.get_between_unsigned(0, self.abilities.len() as u32) as usize)
} }
/// Gets a random hidden ability from the form. /// Gets a random hidden ability from the form.
fn get_random_hidden_ability(&self, rand: &mut Random) -> &StringKey { fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<&StringKey> {
&self.hidden_abilities[rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize] ensure!(!self.hidden_abilities.is_empty(), "No hidden abilities on form");
self.hidden_abilities
.get_res(rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize)
} }
/// Check if the form has a specific flag set. /// Check if the form has a specific flag set.
@ -223,6 +228,8 @@ impl ValueIdentifiable for FormImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
@ -240,12 +247,12 @@ pub(crate) mod tests {
fn hidden_abilities(&self) -> &Vec<StringKey>; fn hidden_abilities(&self) -> &Vec<StringKey>;
fn moves(&self) -> &Box<dyn LearnableMoves>; fn moves(&self) -> &Box<dyn LearnableMoves>;
fn flags(&self) -> &HashSet<StringKey>; fn flags(&self) -> &HashSet<StringKey>;
fn get_type(&self, index: usize) -> TypeIdentifier; fn get_type(&self, index: usize) -> Result<TypeIdentifier>;
fn get_base_stat(&self, stat: Statistic) -> u16; fn get_base_stat(&self, stat: Statistic) -> u16;
fn find_ability_index(&self, ability: &dyn Ability) -> Option<AbilityIndex>; fn find_ability_index(&self, ability: &dyn Ability) -> Option<AbilityIndex>;
fn get_ability(&self, index: AbilityIndex) -> &StringKey; fn get_ability<'a>(&'a self, index: AbilityIndex) -> Result<&'a StringKey>;
fn get_random_ability(&self, rand: &mut Random) -> &StringKey; fn get_random_ability<'a>(&'a self, rand: &mut Random) -> Result<&'a StringKey>;
fn get_random_hidden_ability(&self, rand: &mut Random) -> &StringKey; fn get_random_hidden_ability<'a>(&'a self, rand: &mut Random) -> Result<&'a StringKey>;
fn has_flag(&self, key: &StringKey) -> bool; fn has_flag(&self, key: &StringKey) -> bool;
fn has_flag_by_hash(&self, key_hash: u32) -> bool; fn has_flag_by_hash(&self, key_hash: u32) -> bool;
} }

View File

@ -1,13 +1,14 @@
use anyhow_ext::Result;
use indexmap::IndexSet; use indexmap::IndexSet;
use std::fmt::Debug; use std::fmt::Debug;
use crate::defines::LevelInt; use crate::defines::LevelInt;
use crate::StringKey; use crate::{StringKey, VecExt};
/// The storage of the moves a Pokemon can learn. /// The storage of the moves a Pokemon can learn.
pub trait LearnableMoves: Debug { pub trait LearnableMoves: Debug {
/// Adds a new level move the Pokemon can learn. /// Adds a new level move the Pokemon can learn.
fn add_level_move(&mut self, level: LevelInt, m: &StringKey); fn add_level_move(&mut self, level: LevelInt, m: &StringKey) -> Result<()>;
/// Gets all moves a Pokemon can learn when leveling up to a specific level. /// Gets all moves a Pokemon can learn when leveling up to a specific level.
fn get_learned_by_level(&self, level: LevelInt) -> Option<&Vec<StringKey>>; fn get_learned_by_level(&self, level: LevelInt) -> Option<&Vec<StringKey>>;
/// Gets the distinct moves a Pokemon can learn through leveling up. /// Gets the distinct moves a Pokemon can learn through leveling up.
@ -35,9 +36,10 @@ impl LearnableMovesImpl {
impl LearnableMoves for LearnableMovesImpl { impl LearnableMoves for LearnableMovesImpl {
/// Adds a new level move the Pokemon can learn. /// Adds a new level move the Pokemon can learn.
fn add_level_move(&mut self, level: LevelInt, m: &StringKey) { fn add_level_move(&mut self, level: LevelInt, m: &StringKey) -> Result<()> {
self.learned_by_level.get_mut(level as usize).unwrap().push(m.clone()); self.learned_by_level.get_mut_res(level as usize)?.push(m.clone());
self.distinct_level_moves.insert(m.clone()); self.distinct_level_moves.insert(m.clone());
Ok(())
} }
/// Gets all moves a Pokemon can learn when leveling up to a specific level. /// Gets all moves a Pokemon can learn when leveling up to a specific level.
@ -52,14 +54,16 @@ impl LearnableMoves for LearnableMovesImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
#[test] #[test]
fn adds_level_moves() { fn adds_level_moves() {
let mut moves = LearnableMovesImpl::new(100); let mut moves = LearnableMovesImpl::new(100);
moves.add_level_move(1, &"foo".into()); moves.add_level_move(1, &"foo".into()).unwrap();
moves.add_level_move(1, &"bar".into()); moves.add_level_move(1, &"bar".into()).unwrap();
let m = moves.get_learned_by_level(1u8).unwrap(); let m = moves.get_learned_by_level(1u8).unwrap();
assert_eq!(m.len(), 2); assert_eq!(m.len(), 2);
@ -71,8 +75,8 @@ pub(crate) mod tests {
fn adds_two_same_moves_at_different_level() { fn adds_two_same_moves_at_different_level() {
let mut moves = LearnableMovesImpl::new(100); let mut moves = LearnableMovesImpl::new(100);
moves.add_level_move(1, &"foo".into()); moves.add_level_move(1, &"foo".into()).unwrap();
moves.add_level_move(5, &"foo".into()); moves.add_level_move(5, &"foo".into()).unwrap();
let m = moves.get_learned_by_level(1u8).unwrap(); let m = moves.get_learned_by_level(1u8).unwrap();
assert_eq!(m.len(), 1); assert_eq!(m.len(), 1);

View File

@ -1,3 +1,4 @@
use anyhow_ext::{anyhow, Result};
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
@ -34,7 +35,7 @@ pub trait Species: ValueIdentifiable + Debug {
/// Gets a form by the hash of its name. /// Gets a form by the hash of its name.
fn get_form_by_hash(&self, name_hash: u32) -> Option<Arc<dyn Form>>; fn get_form_by_hash(&self, name_hash: u32) -> Option<Arc<dyn Form>>;
/// Gets the form the Pokemon will have by default, if no other form is specified. /// Gets the form the Pokemon will have by default, if no other form is specified.
fn get_default_form(&self) -> Arc<dyn Form>; fn get_default_form(&self) -> Result<Arc<dyn Form>>;
/// Gets a random gender. /// Gets a random gender.
fn get_random_gender(&self, rand: &mut Random) -> Gender; fn get_random_gender(&self, rand: &mut Random) -> Gender;
/// Check whether the Pokemon has a specific flag set. /// Check whether the Pokemon has a specific flag set.
@ -145,8 +146,13 @@ impl Species for SpeciesImpl {
} }
/// Gets the form the Pokemon will have by default, if no other form is specified. /// Gets the form the Pokemon will have by default, if no other form is specified.
fn get_default_form(&self) -> Arc<dyn Form> { fn get_default_form(&self) -> Result<Arc<dyn Form>> {
self.forms.read().get(&get_default_key()).unwrap().clone() Ok(self
.forms
.read()
.get(&get_default_key())
.ok_or(anyhow!("No default form for species"))?
.clone())
} }
/// Gets a random gender. /// Gets a random gender.
@ -177,6 +183,8 @@ impl ValueIdentifiable for SpeciesImpl {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub(crate) mod tests { pub(crate) mod tests {
use super::*; use super::*;
@ -194,7 +202,7 @@ pub(crate) mod tests {
fn add_form(&self, id: StringKey, form: Arc<dyn Form>); fn add_form(&self, id: StringKey, form: Arc<dyn Form>);
fn get_form(&self, id: &StringKey) -> Option<Arc<dyn Form>>; fn get_form(&self, id: &StringKey) -> Option<Arc<dyn Form>>;
fn get_form_by_hash(&self, name_hash: u32) -> Option<Arc<dyn Form>>; fn get_form_by_hash(&self, name_hash: u32) -> Option<Arc<dyn Form>>;
fn get_default_form(&self) -> Arc<dyn Form>; fn get_default_form(&self) -> Result<Arc<dyn Form>>;
fn get_random_gender(&self, rand: &mut Random) -> Gender; fn get_random_gender(&self, rand: &mut Random) -> Gender;
fn has_flag(&self, key: &StringKey) -> bool; fn has_flag(&self, key: &StringKey) -> bool;
fn has_flag_by_hash(&self, key_hash: u32) -> bool; fn has_flag_by_hash(&self, key_hash: u32) -> bool;

View File

@ -275,10 +275,12 @@ where
T: PrimInt, T: PrimInt,
{ {
/// The lowest value a value on the set can have. /// The lowest value a value on the set can have.
#[allow(clippy::unwrap_used)] // Should never fail
pub fn min() -> T { pub fn min() -> T {
<T as NumCast>::from(MIN).unwrap() <T as NumCast>::from(MIN).unwrap()
} }
/// The highest value a value on the set can have. /// The highest value a value on the set can have.
#[allow(clippy::unwrap_used)] // Should never fail
pub fn max() -> T { pub fn max() -> T {
<T as NumCast>::from(MAX).unwrap() <T as NumCast>::from(MAX).unwrap()
} }

View File

@ -91,6 +91,8 @@ impl Random {
} }
#[cfg(test)] #[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
mod tests { mod tests {
use std::hint::black_box; use std::hint::black_box;
use test::Bencher; use test::Bencher;

View File

@ -29,6 +29,7 @@ static EMPTY: LazyLock<StringKey> = LazyLock::new(|| StringKey::new(""));
impl StringKey { impl StringKey {
#[inline(always)] #[inline(always)]
#[allow(clippy::indexing_slicing)] // Should never fail. More efficient than using get.
/// Gets the hash of a string. /// Gets the hash of a string.
pub const fn get_hash(s: &str) -> u32 { pub const fn get_hash(s: &str) -> u32 {
let mut crc: u32 = 0xffffffff; let mut crc: u32 = 0xffffffff;
@ -119,6 +120,7 @@ impl Display for StringKey {
} }
impl From<&CStr> for StringKey { impl From<&CStr> for StringKey {
#[allow(clippy::unwrap_used)] // Fix later.
fn from(s: &CStr) -> Self { fn from(s: &CStr) -> Self {
StringKey::new(s.to_str().unwrap()) StringKey::new(s.to_str().unwrap())
} }

View File

@ -121,7 +121,9 @@ pub fn load_types(path: &String) -> Box<dyn TypeLibrary> {
for (i, v) in record.iter().skip(1).enumerate() { for (i, v) in record.iter().skip(1).enumerate() {
let effectiveness = v.parse::<f32>().unwrap(); let effectiveness = v.parse::<f32>().unwrap();
type_library.set_effectiveness(offensive_type_id, ((i + 1) as u8).into(), effectiveness); type_library
.set_effectiveness(offensive_type_id, ((i + 1) as u8).into(), effectiveness)
.unwrap();
} }
} }
type_library type_library
@ -463,7 +465,7 @@ fn parse_moves(value: &Value, move_library: &Box<dyn MoveLibrary>) -> Box<dyn Le
let name = level_move.get("name").unwrap().as_str().unwrap().into(); let name = level_move.get("name").unwrap().as_str().unwrap().into();
let level = level_move.get("level").unwrap().as_u64().unwrap() as LevelInt; let level = level_move.get("level").unwrap().as_u64().unwrap() as LevelInt;
assert!(move_library.get(&name).is_some()); assert!(move_library.get(&name).is_some());
moves.add_level_move(level, &name); moves.add_level_move(level, &name).unwrap();
} }
Box::new(moves) Box::new(moves)
@ -500,10 +502,12 @@ fn test_type_library_loaded() {
let types = load_types(&path.to_str().unwrap().to_string()); let types = load_types(&path.to_str().unwrap().to_string());
assert_eq!( assert_eq!(
types.get_effectiveness( types
.get_effectiveness(
types.get_type_id(&"fire".into()).unwrap(), types.get_type_id(&"fire".into()).unwrap(),
&[types.get_type_id(&"grass".into()).unwrap()], &[types.get_type_id(&"grass".into()).unwrap()],
), )
.unwrap(),
2.0 2.0
); );
} }