use std::ops::Deref; use std::sync::Arc; use anyhow::Result; use indexmap::IndexMap; use parking_lot::RwLock; use crate::dynamic_data::script_handling::script::{Script, ScriptContainer}; use crate::{PkmnError, StringKey}; /// A collection of unique scripts. #[derive(Debug, Default)] pub struct ScriptSet { /// The scripts collection. This is an indexmap so we can iterate over them and always get the /// scripts in the same order., while still allowing fast lookup. scripts: RwLock>, } impl ScriptSet { /// Adds a script to the set. If the script with that name already exists in this set, this /// makes that script stack instead. The return value here is that script. pub fn add(&self, script: Arc) -> ScriptContainer { if let Some(lock) = self.scripts.read().get(script.name()) { if let Some(existing) = lock.get() { let existing = existing.read(); if let Some(v) = &*existing { v.stack(); return lock.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 /// set, this makes that script stack instead. The return value here is that script. pub fn stack_or_add(&self, key: &StringKey, instantiation: &F) -> Result> where F: Fn() -> Result>>, { if let Some(lock) = self.scripts.read().get(key) { if let Some(existing) = lock.get() { let existing = existing.read(); if let Some(v) = &*existing { v.stack(); return Ok(Some(lock.clone())); } } } let script = instantiation()?; if let Some(script) = script { let name = script.name().clone(); let arc = ScriptContainer::new(script); self.scripts.write().insert(name, arc.clone()); Ok(Some(arc)) } else { Ok(None) } } /// Gets a script from the set using its unique name. pub fn get(&self, key: &StringKey) -> Option { self.scripts.read().get(key).cloned() } /// Removes a script from the set using its unique name. 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().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) -> Result<()> { for script in self.scripts.read().deref() { if let Some(script) = script.1.get() { let script = script.read(); 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. pub fn has(&self, key: &StringKey) -> bool { self.scripts.read().contains_key(key) } /// Gets a script from the set at a specific index. pub fn at(&self, index: usize) -> Result { 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. pub fn count(&self) -> usize { self.scripts.read().len() } /// Get a vector of the scripts in this set. This copies the current scripts into a Vec, and /// returns that. This allows modifying the scripts in the set, while still being able to iterate /// over them. pub(crate) fn get_owning_iterator(&self) -> Vec { let s = self.scripts.read(); let mut v = Vec::with_capacity(s.deref().len()); for script in s.deref().values() { v.push(script.clone()); } v } }