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)]
#[allow(clippy::unwrap_used)]
#[allow(clippy::indexing_slicing)]
mod tests {
use super::*;
use crate::defines::LevelInt;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -53,7 +53,7 @@ impl PokemonBuilder {
.species()
.get(&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(
self.library,
species,

View File

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

View File

@ -1,5 +1,5 @@
use crate::defines::LevelInt;
use crate::ffi::{BorrowedPtr, ExternPointer, OwnedPtr};
use crate::ffi::{BorrowedPtr, ExternPointer, NativeResult, OwnedPtr};
use crate::static_data::{LearnableMoves, LearnableMovesImpl};
use std::ffi::{c_char, CStr};
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>>,
level: LevelInt,
move_name: BorrowedPtr<c_char>,
) {
ptr.as_mut().add_level_move(level, &CStr::from_ptr(move_name).into())
) -> NativeResult<()> {
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>>,
growth_rate: BorrowedPtr<c_char>,
experience: u32,
) -> LevelInt {
) -> NativeResult<LevelInt> {
ptr.as_ref()
.calculate_level(&CStr::from_ptr(growth_rate).into(), experience)
.into()
}
/// 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(
ptr: ExternPointer<Box<dyn NatureLibrary>>,
seed: u64,
) -> IdentifiablePointer<Arc<dyn Nature>> {
) -> NativeResult<IdentifiablePointer<Arc<dyn Nature>>> {
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.
@ -68,9 +71,12 @@ unsafe extern "C" fn nature_library_get_nature_name(
match nature.as_ref() {
Some(nature) => {
let name = ptr.as_ref().get_nature_name(nature);
match CString::new(name.str()) {
Ok(cstr) => NativeResult::ok(cstr.into_raw()),
Err(_) => NativeResult::err(anyhow!("Failed to convert nature name to C string")),
match name {
Ok(name) => match CString::new(name.str()) {
Ok(cstr) => NativeResult::ok(cstr.into_raw()),
Err(_) => NativeResult::err(anyhow!("Failed to convert nature name to C string")),
},
Err(e) => NativeResult::err(e),
}
}
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>>,
attacking: TypeIdentifier,
defending: TypeIdentifier,
) -> f32 {
ptr.as_ref().get_single_effectiveness(attacking, defending)
) -> NativeResult<f32> {
ptr.as_ref().get_single_effectiveness(attacking, defending).into()
}
/// 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,
defending: OwnedPtr<TypeIdentifier>,
defending_length: usize,
) -> f32 {
) -> NativeResult<f32> {
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.
@ -92,6 +92,8 @@ unsafe extern "C" fn type_library_set_effectiveness(
attacking: TypeIdentifier,
defending: TypeIdentifier,
effectiveness: f32,
) {
ptr.as_mut().set_effectiveness(attacking, defending, effectiveness);
) -> NativeResult<()> {
ptr.as_mut()
.set_effectiveness(attacking, defending, effectiveness)
.into()
}

View File

@ -8,12 +8,12 @@
#![deny(clippy::missing_docs_in_private_items)]
// Linter rules to prevent panics
// Currently still a WIP to fix all of these
// #![deny(clippy::unwrap_used)]
// #![deny(clippy::expect_used)]
// #![deny(clippy::indexing_slicing)]
// #![deny(clippy::string_slice)]
// #![deny(clippy::exit)]
// #![deny(clippy::panic)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::expect_used)]
#![deny(clippy::indexing_slicing)]
#![deny(clippy::string_slice)]
#![deny(clippy::exit)]
#![deny(clippy::panic)]
// Features
#![feature(test)]
#![feature(const_option)]
@ -58,6 +58,7 @@ pub mod static_data;
/// The utils module includes misc utils that are used within PkmnLib
pub mod utils;
use crate::static_data::TypeIdentifier;
use thiserror::Error;
/// 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
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
#[error("The given pointer is not a valid CString")]
InvalidCString,

View File

@ -29,7 +29,7 @@ register! {
defending: u8
) -> f32 {
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)]
#[allow(clippy::indexing_slicing)]
pub(crate) mod tests {
use super::*;

View File

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

View File

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

View File

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

View File

@ -1,5 +1,7 @@
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;
@ -11,9 +13,9 @@ pub trait NatureLibrary: Debug + ValueIdentifiable {
/// Gets a nature by name.
fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn 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.
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.
@ -46,21 +48,28 @@ impl NatureLibrary for NatureLibraryImpl {
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);
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.
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 {
// 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 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)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
#[allow(clippy::expect_used)]
pub mod tests {
use super::*;
use crate::static_data::statistics::Statistic;
@ -93,8 +105,8 @@ pub mod tests {
impl NatureLibrary for NatureLibrary {
fn load_nature(&mut self, name: StringKey, nature: 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_random_nature(&self, rand: &mut Random) -> Arc<dyn Nature>;
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> Result<StringKey>;
fn get_random_nature(&self, rand: &mut Random) -> Result<Arc<dyn Nature>>;
}
impl ValueIdentifiable for NatureLibrary {
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 name = lib.get_nature_name(&n1);
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);
let name2 = lib.get_nature_name(&n2).unwrap();
assert_eq!(name2, "bar".into());
}
@ -148,7 +160,7 @@ pub mod tests {
"foo".into(),
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.decreased_stat(), Statistic::Attack);
assert_eq!(n1.get_stat_modifier(n1.increased_stat()), 1.1);

View File

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

View File

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

View File

@ -1,8 +1,9 @@
use anyhow_ext::Result;
use atomig::Atom;
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
/// 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.
pub trait TypeLibrary: Debug + ValueIdentifiable {
/// 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>;
/// 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.
/// This is equivalent to running [`get_single_effectiveness`] on each defending type, and
/// 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.
fn register_type(&mut self, name: &StringKey) -> TypeIdentifier;
/// 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.
@ -87,19 +99,24 @@ impl TypeLibrary for TypeLibraryImpl {
}
/// Gets the effectiveness for a single attacking type against a single defending type.
fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> f32 {
self.effectiveness[(attacking.val - 1) as usize][(defending.val - 1) as usize]
fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> Result<f32> {
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.
/// This is equivalent to running [`get_single_effectiveness`] on each defending type, and
/// 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;
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.
@ -116,8 +133,19 @@ impl TypeLibrary for TypeLibraryImpl {
}
/// Sets the effectiveness for an attacking type against a defending type.
fn set_effectiveness(&mut self, attacking: TypeIdentifier, defending: TypeIdentifier, effectiveness: f32) {
self.effectiveness[(attacking.val - 1) as usize][(defending.val - 1) as usize] = effectiveness;
fn set_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)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub mod tests {
use assert_approx_eq::assert_approx_eq;
@ -143,8 +173,8 @@ pub mod tests {
let t1 = w.register_type(&"bar".into());
// Drops borrow as mut
w.set_effectiveness(t0, t1, 0.5);
w.set_effectiveness(t1, t0, 2.0);
w.set_effectiveness(t0, t1, 0.5).unwrap();
w.set_effectiveness(t1, t0, 2.0).unwrap();
lib
}
@ -173,14 +203,14 @@ pub mod tests {
let w = &mut lib;
let t0 = w.register_type(&"foo".into());
let t1 = w.register_type(&"bar".into());
w.set_effectiveness(t0, t1, 0.5);
w.set_effectiveness(t1, t0, 2.0);
w.set_effectiveness(t0, t1, 0.5).unwrap();
w.set_effectiveness(t1, t0, 2.0).unwrap();
// Drops borrow as mut
// Borrow as read so we can read
let r = &lib;
assert_approx_eq!(r.get_single_effectiveness(t0, t1), 0.5);
assert_approx_eq!(r.get_single_effectiveness(t1, t0), 2.0);
assert_approx_eq!(r.get_single_effectiveness(t0, t1).unwrap(), 0.5);
assert_approx_eq!(r.get_single_effectiveness(t1, t0).unwrap(), 2.0);
}
#[test]
@ -191,14 +221,14 @@ pub mod tests {
let w = &mut lib;
let t0 = w.register_type(&"foo".into());
let t1 = w.register_type(&"bar".into());
w.set_effectiveness(t0, t1, 0.5);
w.set_effectiveness(t1, t0, 2.0);
w.set_effectiveness(t0, t1, 0.5).unwrap();
w.set_effectiveness(t1, t0, 2.0).unwrap();
// Drops borrow as mut
// Borrow as read so we can read
let r = &lib;
assert_approx_eq!(r.get_effectiveness(t0, &[t1, t1]), 0.25);
assert_approx_eq!(r.get_effectiveness(t1, &[t0, t0]), 4.0);
assert_approx_eq!(r.get_effectiveness(t0, &[t1, t1]).unwrap(), 0.25);
assert_approx_eq!(r.get_effectiveness(t1, &[t0, t0]).unwrap(), 4.0);
}
#[test]

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
use anyhow_ext::{ensure, Result};
use hashbrown::HashSet;
use std::fmt::Debug;
@ -5,8 +6,8 @@ use crate::static_data::Statistic;
use crate::static_data::TypeIdentifier;
use crate::static_data::{Ability, StaticStatisticSet};
use crate::static_data::{AbilityIndex, LearnableMoves};
use crate::StringKey;
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
/// many more.
@ -34,7 +35,7 @@ pub trait Form: ValueIdentifiable + Debug {
fn flags(&self) -> &HashSet<StringKey>;
/// 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.
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>;
/// 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.
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.
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.
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.
fn get_type(&self, index: usize) -> TypeIdentifier {
self.types[index]
fn get_type(&self, index: usize) -> Result<TypeIdentifier> {
Ok(*self.types.get_res(index)?)
}
/// Gets a single base stat value.
@ -189,21 +190,25 @@ impl Form for FormImpl {
}
/// Gets an ability from the form.
fn get_ability(&self, index: AbilityIndex) -> &StringKey {
fn get_ability(&self, index: AbilityIndex) -> Result<&StringKey> {
if index.hidden {
&self.hidden_abilities[index.index as usize]
Ok(self.hidden_abilities.get_res(index.index as usize)?)
} else {
&self.abilities[index.index as usize]
Ok(self.abilities.get_res(index.index as usize)?)
}
}
/// Gets a random ability from the form.
fn get_random_ability(&self, rand: &mut Random) -> &StringKey {
&self.abilities[rand.get_between_unsigned(0, self.abilities.len() as u32) as usize]
fn get_random_ability(&self, rand: &mut Random) -> Result<&StringKey> {
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.
fn get_random_hidden_ability(&self, rand: &mut Random) -> &StringKey {
&self.hidden_abilities[rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize]
fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<&StringKey> {
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.
@ -223,6 +228,8 @@ impl ValueIdentifiable for FormImpl {
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub(crate) mod tests {
use super::*;
@ -240,12 +247,12 @@ pub(crate) mod tests {
fn hidden_abilities(&self) -> &Vec<StringKey>;
fn moves(&self) -> &Box<dyn LearnableMoves>;
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 find_ability_index(&self, ability: &dyn Ability) -> Option<AbilityIndex>;
fn get_ability(&self, index: AbilityIndex) -> &StringKey;
fn get_random_ability(&self, rand: &mut Random) -> &StringKey;
fn get_random_hidden_ability(&self, rand: &mut Random) -> &StringKey;
fn get_ability<'a>(&'a self, index: AbilityIndex) -> Result<&'a StringKey>;
fn get_random_ability<'a>(&'a self, rand: &mut Random) -> Result<&'a 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_by_hash(&self, key_hash: u32) -> bool;
}

View File

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

View File

@ -1,3 +1,4 @@
use anyhow_ext::{anyhow, Result};
use std::fmt::Debug;
use std::sync::{Arc, LazyLock};
@ -34,7 +35,7 @@ pub trait Species: ValueIdentifiable + Debug {
/// Gets a form by the hash of its name.
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.
fn get_default_form(&self) -> Arc<dyn Form>;
fn get_default_form(&self) -> Result<Arc<dyn Form>>;
/// Gets a random gender.
fn get_random_gender(&self, rand: &mut Random) -> Gender;
/// 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.
fn get_default_form(&self) -> Arc<dyn Form> {
self.forms.read().get(&get_default_key()).unwrap().clone()
fn get_default_form(&self) -> Result<Arc<dyn Form>> {
Ok(self
.forms
.read()
.get(&get_default_key())
.ok_or(anyhow!("No default form for species"))?
.clone())
}
/// Gets a random gender.
@ -177,6 +183,8 @@ impl ValueIdentifiable for SpeciesImpl {
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::unwrap_used)]
pub(crate) mod tests {
use super::*;
@ -194,7 +202,7 @@ pub(crate) mod tests {
fn add_form(&self, id: StringKey, form: 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_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 has_flag(&self, key: &StringKey) -> bool;
fn has_flag_by_hash(&self, key_hash: u32) -> bool;

View File

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

View File

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

View File

@ -29,6 +29,7 @@ static EMPTY: LazyLock<StringKey> = LazyLock::new(|| StringKey::new(""));
impl StringKey {
#[inline(always)]
#[allow(clippy::indexing_slicing)] // Should never fail. More efficient than using get.
/// Gets the hash of a string.
pub const fn get_hash(s: &str) -> u32 {
let mut crc: u32 = 0xffffffff;
@ -119,6 +120,7 @@ impl Display for StringKey {
}
impl From<&CStr> for StringKey {
#[allow(clippy::unwrap_used)] // Fix later.
fn from(s: &CStr) -> Self {
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() {
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
@ -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 level = level_move.get("level").unwrap().as_u64().unwrap() as LevelInt;
assert!(move_library.get(&name).is_some());
moves.add_level_move(level, &name);
moves.add_level_move(level, &name).unwrap();
}
Box::new(moves)
@ -500,10 +502,12 @@ fn test_type_library_loaded() {
let types = load_types(&path.to_str().unwrap().to_string());
assert_eq!(
types.get_effectiveness(
types.get_type_id(&"fire".into()).unwrap(),
&[types.get_type_id(&"grass".into()).unwrap()],
),
types
.get_effectiveness(
types.get_type_id(&"fire".into()).unwrap(),
&[types.get_type_id(&"grass".into()).unwrap()],
)
.unwrap(),
2.0
);
}