A bunch more work on replacing every potential panic with results
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2023-04-16 19:57:21 +02:00
parent 1b8403ecda
commit 00d596d656
37 changed files with 526 additions and 300 deletions

View File

@@ -1,7 +1,7 @@
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::script_handling::ScriptSource;
use crate::dynamic_data::Pokemon;
use crate::{script_hook, PkmnError, ValueIdentifiable, ValueIdentifier};
use crate::{script_hook, PkmnError, ValueIdentifiable, ValueIdentifier, VecExt};
use anyhow::Result;
use anyhow_ext::anyhow;
use parking_lot::lock_api::MappedRwLockReadGuard;
@@ -81,9 +81,7 @@ impl ChoiceQueue {
let len = self.queue.read().len();
let mut write_lock = self.queue.write();
for index in self.current..len {
let choice = &mut write_lock
.get_mut(index)
.ok_or(PkmnError::IndexOutOfBounds { index, len })?;
let choice = &mut write_lock.get_mut_res(index)?;
if let Some(choice) = choice {
let mut speed = choice.user().boosted_stats().speed();
script_hook!(change_speed, (*choice), choice, &mut speed);
@@ -121,14 +119,9 @@ impl ChoiceQueue {
return Ok(true);
}
let len = queue_lock.len();
// Take the choice we want to move forward out of it's place.
let choice = queue_lock
.get_mut(desired_index)
.ok_or(PkmnError::IndexOutOfBounds {
index: self.current,
len,
})?
.get_mut_res(desired_index)?
.take()
.ok_or(anyhow!("Choice was already taken"))?;
// Iterate backwards from the spot before the choice we want to move up, push them all back
@@ -136,15 +129,8 @@ impl ChoiceQueue {
for index in (self.current..desired_index).rev() {
queue_lock.swap(index, index + 1);
}
let len = queue_lock.len();
// Place the choice that needs to be next in the next to be executed position.
let _ = queue_lock
.get_mut(self.current)
.ok_or(PkmnError::IndexOutOfBounds {
index: self.current,
len,
})?
.insert(choice);
let _ = queue_lock.get_mut_res(self.current)?.insert(choice);
true
}
None => false,
@@ -173,13 +159,13 @@ impl ValueIdentifiable for ChoiceQueue {
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use crate::defines::LevelInt;
use crate::dynamic_data::{DynamicLibrary, PassChoice};
use crate::static_data::{AbilityIndex, Gender};
use std::sync::Arc;
#[test]
fn create_empty_queue() {
let queue = ChoiceQueue::new(Vec::new());

View File

@@ -7,9 +7,9 @@ use num_traits::abs;
use crate::dynamic_data::Battle;
use crate::dynamic_data::Pokemon;
use crate::static_data::MoveTarget;
use crate::PkmnError;
use crate::VecExt;
/// Helper type for the vector of targets we will return.
/// Helper type for the vector of targ ets we will return.
pub type TargetList = Vec<Option<Arc<Pokemon>>>;
/// This returns all Pokemon in the battle.
@@ -127,16 +127,7 @@ pub fn resolve_targets(side: u8, index: u8, target: MoveTarget, battle: &Battle)
// the client deal with what side is passed.
MoveTarget::AllAlly | MoveTarget::AllOpponent => {
let mut v = Vec::new();
for pokemon in battle
.sides()
.get(side as usize)
.ok_or(PkmnError::IndexOutOfBounds {
index: side as usize,
len: battle.sides().len(),
})?
.pokemon()
.deref()
{
for pokemon in battle.sides().get_res(side as usize)?.pokemon().deref() {
v.push(pokemon.as_ref().cloned());
}
v

View File

@@ -20,7 +20,7 @@ use crate::dynamic_data::VolatileScriptsOwner;
use crate::dynamic_data::{is_valid_target, ScriptWrapper};
use crate::dynamic_data::{ChoiceQueue, ScriptContainer};
use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData};
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier};
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier, VecExt};
/// A pokemon battle, with any amount of sides and pokemon per side.
#[derive(Debug)]
@@ -271,13 +271,7 @@ impl Battle {
let side = choice.user().get_battle_side_index();
match side {
Some(side) => {
self.sides
.get(side as usize)
.ok_or(PkmnError::IndexOutOfBounds {
index: side as usize,
len: self.sides.len(),
})?
.set_choice(choice)?;
self.sides.get_res(side as usize)?.set_choice(choice)?;
self.check_choices_set_and_run()?;
Ok(true)
}

View File

@@ -1,3 +1,4 @@
use anyhow::Result;
use std::sync::Arc;
use crate::dynamic_data::models::pokemon::Pokemon;
@@ -19,12 +20,12 @@ pub struct BattleParty {
impl BattleParty {
/// Initializes a battle party with the underlying party, and the indices the party is responsible
/// for.
pub fn new(party: Arc<PokemonParty>, responsible_indices: Vec<(u8, u8)>) -> Self {
Self {
pub fn new(party: Arc<PokemonParty>, responsible_indices: Vec<(u8, u8)>) -> Result<Self> {
Ok(Self {
identifier: Default::default(),
party,
responsible_indices,
}
})
}
/// Checks whether the party is responsible for the given index.

View File

@@ -14,7 +14,7 @@ use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, Scrip
use crate::dynamic_data::Script;
use crate::dynamic_data::ScriptSet;
use crate::dynamic_data::VolatileScriptsOwner;
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier};
use crate::{script_hook, StringKey, ValueIdentifiable, ValueIdentifier, VecExt};
/// A side on a battle.
#[derive(Debug)]
@@ -149,12 +149,7 @@ impl BattleSide {
for (index, pokemon_slot) in self.pokemon.read().iter().enumerate() {
if let Some(pokemon) = pokemon_slot {
if std::ptr::eq(pokemon.deref(), choice.user().deref()) {
let len = self.choices.read().len();
self.choices
.write()
.get_mut(index)
.ok_or(PkmnError::IndexOutOfBounds { index, len })?
.replace(choice);
self.choices.write().get_mut_res(index)?.replace(choice);
self.choices_set.fetch_add(1, Ordering::SeqCst);
return Ok(());
}
@@ -180,10 +175,7 @@ impl BattleSide {
pub fn set_pokemon(&self, index: u8, pokemon: Option<Arc<Pokemon>>) -> Result<()> {
{
let mut write_lock = self.pokemon.write();
let old = write_lock.get_mut(index as usize).ok_or(PkmnError::IndexOutOfBounds {
index: index as usize,
len: self.pokemon_per_side as usize,
})?;
let old = write_lock.get_mut_res(index as usize)?;
let old = match pokemon {
Some(pokemon) => old.replace(pokemon),
None => old.take(),
@@ -193,23 +185,17 @@ impl BattleSide {
// side again.
drop(write_lock);
script_hook!(on_remove, old_pokemon,);
old_pokemon.set_on_battlefield(false);
old_pokemon.set_on_battlefield(false)?;
}
}
let pokemon = {
let read_lock = self.pokemon.read();
&read_lock
.get(index as usize)
.ok_or(PkmnError::IndexOutOfBounds {
index: index as usize,
len: read_lock.len(),
})?
.clone()
&read_lock.get_res(index as usize)?.clone()
};
if let Some(pokemon) = pokemon {
pokemon.set_battle_data(self.battle, self.index);
pokemon.set_on_battlefield(true);
pokemon.set_on_battlefield(true)?;
pokemon.set_battle_index(index);
let battle = self.battle()?;
@@ -252,11 +238,7 @@ impl BattleSide {
/// If this happens, the slot can not be used again.
pub(crate) fn mark_slot_as_unfillable(&self, index: u8) -> Result<()> {
self.fillable_slots
.get(index as usize)
.ok_or(PkmnError::IndexOutOfBounds {
index: index as usize,
len: self.fillable_slots.len(),
})?
.get_res(index as usize)?
.store(false, Ordering::SeqCst);
Ok(())
}
@@ -266,14 +248,7 @@ impl BattleSide {
for (i, slot) in self.pokemon.read().iter().enumerate() {
if let Some(p) = slot {
if std::ptr::eq(p.deref().deref(), pokemon.deref()) {
return Ok(self
.fillable_slots
.get(i)
.ok_or(PkmnError::IndexOutOfBounds {
index: i,
len: self.fillable_slots.len(),
})?
.load(Ordering::Relaxed));
return Ok(self.fillable_slots.get_res(i)?.load(Ordering::Relaxed));
}
}
}

View File

@@ -22,7 +22,7 @@ use crate::static_data::TypeIdentifier;
use crate::static_data::{Ability, Statistic};
use crate::static_data::{ClampedStatisticSet, StatisticSet};
use crate::utils::Random;
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier};
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier, VecExt};
use anyhow::{anyhow, bail, Result};
/// An individual Pokemon as we know and love them.
@@ -595,16 +595,17 @@ impl Pokemon {
}
/// Sets whether or not the Pokemon is on the battlefield.
pub fn set_on_battlefield(&self, value: bool) {
pub fn set_on_battlefield(&self, value: bool) -> Result<()> {
let r = self.battle_data.read();
if let Some(data) = &mut r.deref() {
data.on_battle_field.store(value, Ordering::SeqCst);
if !value {
self.volatile.clear();
self.volatile.clear()?;
self.weight.store(self.form().weight(), Ordering::SeqCst);
self.height.store(self.form().height(), Ordering::SeqCst);
}
}
Ok(())
}
/// Sets the index of the slot of the side the Pokemon is on.
@@ -676,11 +677,7 @@ impl Pokemon {
if !battle.can_slot_be_filled(battle_data.battle_side_index(), battle_data.index()) {
battle
.sides()
.get(battle_data.battle_side_index() as usize)
.ok_or(PkmnError::IndexOutOfBounds {
index: battle_data.battle_side_index() as usize,
len: battle.sides().len(),
})?
.get_res(battle_data.battle_side_index() as usize)?
.mark_slot_as_unfillable(battle_data.index())?;
}
@@ -721,9 +718,12 @@ impl Pokemon {
pub fn learn_move(&self, move_name: &StringKey, learn_method: MoveLearnMethod) -> Result<()> {
let mut learned_moves = self.learned_moves().write();
let move_pos = learned_moves.iter().position(|a| a.is_none());
if move_pos.is_none() {
bail!("No more moves with an empty space found.");
}
let move_pos = match move_pos {
Some(a) => a,
None => {
bail!("No more moves with an empty space found.");
}
};
let move_data = self
.library
.static_data()
@@ -732,7 +732,9 @@ impl Pokemon {
.ok_or(PkmnError::InvalidMoveName {
move_name: move_name.clone(),
})?;
learned_moves[move_pos.unwrap()] = Some(Arc::new(LearnedMove::new(move_data, learn_method)));
learned_moves
.get_mut_res(move_pos)?
.replace(Arc::new(LearnedMove::new(move_data, learn_method)));
Ok(())
}
@@ -805,7 +807,10 @@ impl ScriptSource for Pokemon {
let mut c = 3;
if let Some(battle_data) = &self.battle_data.read().deref() {
if let Some(battle) = battle_data.battle() {
c += battle.sides()[battle_data.battle_side_index() as usize].get_script_count()?;
c += battle
.sides()
.get_res(battle_data.battle_side_index() as usize)?
.get_script_count()?;
}
}
Ok(c)
@@ -826,7 +831,10 @@ impl ScriptSource for Pokemon {
self.get_own_scripts(scripts);
if let Some(battle_data) = &self.battle_data.read().deref() {
if let Some(battle) = battle_data.battle() {
battle.sides()[battle_data.battle_side_index() as usize].collect_scripts(scripts)?;
battle
.sides()
.get_res(battle_data.battle_side_index() as usize)?
.collect_scripts(scripts)?;
}
}
Ok(())
@@ -862,6 +870,7 @@ pub enum DamageSource {
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
pub mod test {
use crate::dynamic_data::libraries::test::MockDynamicLibrary;
use crate::dynamic_data::models::pokemon::Pokemon;

View File

@@ -5,7 +5,7 @@ use crate::dynamic_data::models::learned_move::MoveLearnMethod;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::DynamicLibrary;
use crate::static_data::{AbilityIndex, Gender};
use crate::{Random, StringKey};
use crate::{PkmnError, Random, StringKey};
use anyhow::Result;
/// This allows for the easy chain building of a Pokemon.
@@ -47,7 +47,12 @@ impl PokemonBuilder {
Random::default()
};
let species = self.library.static_data().species().get(&self.species).unwrap();
let species = self
.library
.static_data()
.species()
.get(&self.species)
.ok_or(PkmnError::InvalidSpeciesName { species: self.species })?;
let form = species.get_default_form();
let p = Pokemon::new(
self.library,

View File

@@ -1,9 +1,10 @@
use anyhow::Result;
use parking_lot::lock_api::RwLockReadGuard;
use parking_lot::{RawRwLock, RwLock};
use std::sync::Arc;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::{ValueIdentifiable, ValueIdentifier};
use crate::{ValueIdentifiable, ValueIdentifier, VecExt};
/// A list of Pokemon belonging to a trainer.
#[derive(Debug)]
@@ -52,14 +53,17 @@ impl PokemonParty {
}
/// Sets the Pokemon at an index to a Pokemon, returning the old Pokemon.
pub fn swap_into(&self, index: usize, pokemon: Option<Arc<Pokemon>>) -> Option<Arc<Pokemon>> {
pub fn swap_into(&self, index: usize, pokemon: Option<Arc<Pokemon>>) -> Result<Option<Arc<Pokemon>>> {
let mut party = self.pokemon.write();
if index >= party.len() {
return pokemon;
return Ok(pokemon);
}
let old = party[index].as_ref().cloned();
party[index] = pokemon;
old
let value = party.get_mut_res(index)?;
let old = match pokemon {
Some(p) => value.replace(p),
None => value.take(),
};
Ok(old)
}
/// Whether or not the party still has Pokemon that can be used in battle.
@@ -83,18 +87,18 @@ impl PokemonParty {
}
/// Makes sure there are no empty spots in the party anymore, leaving the length the same.
pub fn pack_party(&self) {
pub fn pack_party(&self) -> Result<()> {
let mut first_empty = None;
let mut i = 0;
let mut party = self.pokemon.write();
loop {
if party[i].is_none() {
if party.get_res(i)?.is_none() {
if first_empty.is_none() {
first_empty = Some(i)
}
} else if first_empty.is_some() {
party.swap(first_empty.unwrap(), i);
i = first_empty.unwrap();
} else if let Some(f) = first_empty {
party.swap(f, i);
i = f;
first_empty = None;
}
i += 1;
@@ -102,6 +106,7 @@ impl PokemonParty {
break;
}
}
Ok(())
}
/// Checks if the party contains a given pokemon.

View File

@@ -1,8 +1,9 @@
use anyhow::Result;
use anyhow::{anyhow, Result};
use std::sync::{Arc, Weak};
use parking_lot::RwLock;
use crate::VecExt;
#[doc(inline)]
pub use item_script::*;
#[doc(inline)]
@@ -27,7 +28,7 @@ mod volatile_scripts_owner;
macro_rules! script_hook {
($hook_name: ident, $source: expr, $($parameters: expr),*) => {
let mut aggregator = $source.get_script_iterator()?;
while let Some(script_container) = aggregator.get_next() {
while let Some(script_container) = aggregator.get_next()? {
let script = script_container.get();
if let Some(script) = script {
if let Some(script) = script.read().as_deref() {
@@ -157,16 +158,16 @@ impl ScriptIterator {
}
/// Move to the next valid value in the scripts.
fn increment_to_next_value(&mut self) -> bool {
fn increment_to_next_value(&mut self) -> Result<bool> {
if self.index != -1 {
let wrapper = unsafe { &(*self.scripts)[self.index as usize] };
let wrapper = unsafe { &(*self.scripts).get_res(self.index as usize)? };
if let ScriptWrapper::Set(set) = wrapper {
if let Some(set) = set.upgrade() {
self.set_index += 1;
if self.set_index as usize >= set.count() {
self.set_index = -1;
} else {
return true;
return Ok(true);
}
}
}
@@ -175,41 +176,61 @@ impl ScriptIterator {
let len = (unsafe { &*self.scripts }).len() as i32;
for index in self.index..len {
self.index = index;
let wrapper = unsafe { &self.scripts.as_ref().unwrap()[self.index as usize] };
let wrapper = unsafe {
&self
.scripts
.as_ref()
.ok_or(anyhow!("ScriptIterator scripts pointer is null"))?
.get_res(self.index as usize)?
};
if let ScriptWrapper::Set(s) = wrapper {
if let Some(set) = s.upgrade() {
if set.count() > 0 {
self.set_index = 0;
return true;
return Ok(true);
}
}
} else if let ScriptWrapper::Script(script) = wrapper {
if let Some(v) = script.upgrade() {
if let Some(..) = v.read().as_ref() {
return true;
return Ok(true);
}
}
}
}
false
Ok(false)
}
/// Gets the next valid script. If none is found, returns None.
pub fn get_next(&mut self) -> Option<ScriptContainer> {
if !self.increment_to_next_value() {
return None;
pub fn get_next(&mut self) -> Result<Option<ScriptContainer>> {
if !self.increment_to_next_value()? {
return Ok(None);
}
unsafe {
return match &self.scripts.as_ref().unwrap()[self.index as usize] {
// increment_to_next_value
ScriptWrapper::Script(script) => Some(script.upgrade().unwrap().into()),
ScriptWrapper::Set(set) => {
let set = set.upgrade().unwrap();
let sc = set.at(self.set_index as usize);
return Some(sc);
}
};
Ok(
match &self
.scripts
.as_ref()
.ok_or(anyhow!("ScriptIterator scripts pointer is null"))?
.get_res(self.index as usize)?
{
// increment_to_next_value
ScriptWrapper::Script(script) => Some(
script
.upgrade()
.ok_or(anyhow!("Couldn't get a strong reference to a script"))?
.into(),
),
ScriptWrapper::Set(set) => {
let set = set
.upgrade()
.ok_or(anyhow!("Couldn't get a strong reference to a set"))?;
let sc = set.at(self.set_index as usize)?;
return Ok(Some(sc));
}
},
)
}
}
@@ -221,6 +242,7 @@ impl ScriptIterator {
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use std::any::Any;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
@@ -291,10 +313,10 @@ mod tests {
let script = ScriptContainer::new(Arc::new(TestScript::new()));
let scripts = vec![ScriptWrapper::from(&script)];
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
while let Some(v) = aggregator.get_next() {
while let Some(v) = aggregator.get_next().unwrap() {
v.get().unwrap().read().as_ref().unwrap().stack();
}
let a = script.get_as::<TestScript>();
let a = script.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), 1);
}
@@ -305,10 +327,10 @@ mod tests {
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
for i in 1..11 {
aggregator.reset();
while let Some(v) = aggregator.get_next() {
while let Some(v) = aggregator.get_next().unwrap() {
v.get().unwrap().read().as_ref().unwrap().stack();
}
let a = script.get_as::<TestScript>();
let a = script.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), i);
}
}
@@ -324,14 +346,14 @@ mod tests {
ScriptWrapper::from(&script3),
];
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
while let Some(v) = aggregator.get_next() {
while let Some(v) = aggregator.get_next().unwrap() {
v.get().unwrap().read().as_ref().unwrap().stack();
}
let a = script1.get_as::<TestScript>();
let a = script1.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), 1);
let a = script2.get_as::<TestScript>();
let a = script2.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), 1);
let a = script3.get_as::<TestScript>();
let a = script3.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), 1);
}
@@ -348,14 +370,14 @@ mod tests {
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
for i in 1..11 {
aggregator.reset();
while let Some(v) = aggregator.get_next() {
while let Some(v) = aggregator.get_next().unwrap() {
v.get().unwrap().read().as_ref().unwrap().stack();
}
let a = script1.get_as::<TestScript>();
let a = script1.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), i);
let a = script2.get_as::<TestScript>();
let a = script2.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), i);
let a = script3.get_as::<TestScript>();
let a = script3.get_as::<TestScript>().unwrap();
assert_eq!(a.test_count.load(Ordering::Relaxed), i);
}
}
@@ -371,19 +393,19 @@ mod tests {
let mut aggregator = ScriptIterator::new(&scripts as *const Vec<ScriptWrapper>);
for i in 1..11 {
aggregator.reset();
while let Some(v) = aggregator.get_next() {
while let Some(v) = aggregator.get_next().unwrap() {
v.get().unwrap().read().as_ref().unwrap().stack();
}
let s = set.at(0);
let s = s.get_as::<TestScript>();
let s = set.at(0).unwrap();
let s = s.get_as::<TestScript>().unwrap();
assert_eq!(s.test_count.load(Ordering::Relaxed), i);
assert_eq!(s.name().str(), "test_a");
let s = set.at(1);
let s = s.get_as::<TestScript>();
let s = set.at(1).unwrap();
let s = s.get_as::<TestScript>().unwrap();
assert_eq!(s.test_count.load(Ordering::Relaxed), i);
assert_eq!(s.name().str(), "test_b");
let s = set.at(2);
let s = s.get_as::<TestScript>();
let s = set.at(2).unwrap();
let s = s.get_as::<TestScript>().unwrap();
assert_eq!(s.test_count.load(Ordering::Relaxed), i);
assert_eq!(s.name().str(), "test_c");
}
@@ -402,6 +424,7 @@ mod tests {
aggregator
.get_next()
.unwrap()
.unwrap()
.get()
.unwrap()
.read()
@@ -415,6 +438,7 @@ mod tests {
aggregator
.get_next()
.unwrap()
.unwrap()
.get()
.unwrap()
.read()
@@ -425,8 +449,8 @@ mod tests {
"test_b"
);
set.remove(&"test_c".into());
assert!(aggregator.get_next().is_none());
set.remove(&"test_c".into()).unwrap();
assert!(aggregator.get_next().unwrap().is_none());
}
#[test]
@@ -442,6 +466,7 @@ mod tests {
aggregator
.get_next()
.unwrap()
.unwrap()
.get()
.unwrap()
.read()
@@ -452,12 +477,13 @@ mod tests {
"test_a"
);
set.remove(&"test_b".into());
set.remove(&"test_b".into()).unwrap();
assert_eq!(
aggregator
.get_next()
.unwrap()
.unwrap()
.get()
.unwrap()
.read()
@@ -467,7 +493,7 @@ mod tests {
.str(),
"test_c"
);
assert!(aggregator.get_next().is_none());
assert!(aggregator.get_next().unwrap().is_none());
}
pub struct TestScriptSource {
@@ -503,10 +529,10 @@ mod tests {
let mut aggregator = source.get_script_iterator().unwrap();
aggregator.reset();
assert!(aggregator.get_next().is_none());
assert!(aggregator.get_next().unwrap().is_none());
aggregator.reset();
source.script.set(Arc::new(TestScript::new()));
assert!(aggregator.get_next().is_some());
assert!(aggregator.get_next().unwrap().is_some());
}
#[test]
@@ -518,9 +544,9 @@ mod tests {
let mut aggregator = source.get_script_iterator().unwrap();
source.script.set(Arc::new(TestScript::new()));
assert!(aggregator.get_next().is_some());
assert!(aggregator.get_next().unwrap().is_some());
aggregator.reset();
source.script.clear();
assert!(aggregator.get_next().is_none());
assert!(aggregator.get_next().unwrap().is_none());
}
}

View File

@@ -1,3 +1,4 @@
use anyhow::{anyhow, Result};
use std::any::Any;
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
@@ -355,12 +356,16 @@ impl ScriptContainer {
}
/// Get the underlying script as the downcasted value.
pub fn get_as<T: 'static>(&self) -> MappedRwLockReadGuard<T> {
RwLockReadGuard::map(self.script.read(), |a| unsafe {
let ptr = a.as_ref().as_ref().unwrap().as_ref() as *const dyn Script;
let any = ptr.as_ref().unwrap().as_any();
any.downcast_ref::<T>().unwrap()
})
pub fn get_as<T: 'static>(&self) -> Result<MappedRwLockReadGuard<T>> {
let r = RwLockReadGuard::try_map(self.script.read(), |a| unsafe {
let ptr = a.as_ref().as_ref()?.as_ref() as *const dyn Script;
let any = ptr.as_ref()?.as_any();
any.downcast_ref::<T>()
});
match r {
Ok(a) => Ok(a),
Err(_) => Err(anyhow!("Could not downcast script to requested type")),
}
}
}

View File

@@ -6,7 +6,7 @@ use indexmap::IndexMap;
use parking_lot::RwLock;
use crate::dynamic_data::script_handling::script::{Script, ScriptContainer};
use crate::StringKey;
use crate::{PkmnError, StringKey};
/// A collection of unique scripts.
#[derive(Debug, Default)]
@@ -29,10 +29,12 @@ impl ScriptSet {
}
}
}
self.scripts
.write()
.insert(script.name().clone(), ScriptContainer::new(script));
self.scripts.read().last().unwrap().1.clone()
// If the existing script does not exist, or is not a script, we can just add the new one.
let name = script.name().clone();
let container = ScriptContainer::new(script);
self.scripts.write().insert(name, container.clone());
container
}
/// Adds a script with a name to the set. If the script with that name already exists in this
@@ -54,8 +56,8 @@ impl ScriptSet {
if let Some(script) = script {
let name = script.name().clone();
let arc = ScriptContainer::new(script);
self.scripts.write().insert(name, arc);
Ok(Some(self.scripts.read().last().unwrap().1.clone()))
self.scripts.write().insert(name, arc.clone());
Ok(Some(arc))
} else {
Ok(None)
}
@@ -67,27 +69,34 @@ impl ScriptSet {
}
/// Removes a script from the set using its unique name.
pub fn remove(&self, key: &StringKey) {
pub fn remove(&self, key: &StringKey) -> Result<()> {
let value = self.scripts.write().shift_remove(key);
if let Some(script) = value {
if let Some(script) = script.get() {
let script = script.read();
script.as_ref().unwrap().on_remove();
script.as_ref().unwrap().mark_for_deletion();
script.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.on_remove();
script
.as_ref()
.ok_or(PkmnError::UnableToAcquireLock)?
.mark_for_deletion();
}
}
Ok(())
}
/// Clears all scripts from the set.
pub fn clear(&self) {
pub fn clear(&self) -> Result<()> {
for script in self.scripts.read().deref() {
if let Some(script) = script.1.get() {
let script = script.read();
script.as_ref().unwrap().on_remove();
script.as_ref().unwrap().mark_for_deletion();
if let Some(script) = &*script {
script.on_remove();
script.mark_for_deletion();
}
}
}
self.scripts.write().clear();
Ok(())
}
/// Checks if the set has a script with the given name.
@@ -96,8 +105,17 @@ impl ScriptSet {
}
/// Gets a script from the set at a specific index.
pub fn at(&self, index: usize) -> ScriptContainer {
self.scripts.read()[index].clone()
pub fn at(&self, index: usize) -> Result<ScriptContainer> {
Ok(self
.scripts
.read()
.get_index(index)
.ok_or(PkmnError::IndexOutOfBounds {
index,
len: self.scripts.read().len(),
})?
.1
.clone())
}
/// Gets the number of scripts in the set.

View File

@@ -35,7 +35,7 @@ pub trait VolatileScriptsOwner {
}
/// Removes a volatile script by name.
fn remove_volatile_script(&self, key: &StringKey) {
fn remove_volatile_script(&self, key: &StringKey) -> Result<()> {
self.volatile_scripts().remove(key)
}
}