PkmnLib_rs/src/dynamic_data/script_handling/script_set.rs

168 lines
5.8 KiB
Rust
Executable File

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<IndexMap<StringKey, ScriptContainer>>,
}
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<dyn Script>) -> Result<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 Ok(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());
Ok(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<F>(&self, key: &StringKey, instantiation: &F) -> Result<Option<ScriptContainer>>
where
F: Fn() -> Result<Option<Arc<dyn Script>>>,
{
if let Some(lock) = self.scripts.read().get(key) {
if let Some(existing) = lock.get() {
let existing = existing.read();
if let Some(v) = &*existing {
let script_result = v.stack();
match script_result {
Ok(_) => (),
Err(e) => {
crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
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<ScriptContainer> {
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();
let script_result = script.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.on_remove();
match script_result {
Ok(_) => (),
Err(e) => {
crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
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 {
let script_result = script.on_remove();
match script_result {
Ok(_) => (),
Err(e) => {
crate::dynamic_data::script_handling::handle_script_error(&e);
}
}
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<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.
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<ScriptContainer> {
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
}
/// Gets the names of all scripts in the set.
pub fn get_script_names(&self) -> Vec<StringKey> {
let s = self.scripts.read();
let mut v = Vec::with_capacity(s.deref().len());
for script in s.deref().keys() {
v.push(script.clone());
}
v
}
}