use crate::prelude::*;
use bevy_platform::collections::HashMap;
use bevy_platform::sync::{Arc, RwLock};
use core::any::Any;
use core::error::Error;
use core::fmt::{self, Debug, Display};
#[allow(missing_docs)]
pub type Result<T> = core::result::Result<T, VariableStorageError>;
pub trait VariableStorage: Debug + Send + Sync {
fn clone_shallow(&self) -> Box<dyn VariableStorage>;
fn set(&mut self, name: String, value: YarnValue) -> Result<()>;
fn get(&self, name: &str) -> Result<YarnValue>;
fn contains(&self, name: &str) -> bool {
self.get(name).is_ok()
}
fn extend(&mut self, values: HashMap<String, YarnValue>) -> Result<()>;
fn variables(&self) -> HashMap<String, YarnValue>;
fn clear(&mut self);
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl Extend<(String, YarnValue)> for Box<dyn VariableStorage> {
fn extend<T: IntoIterator<Item = (String, YarnValue)>>(&mut self, iter: T) {
let hash_map = iter.into_iter().collect();
VariableStorage::extend(self.as_mut(), hash_map)
.unwrap_or_else(|e| panic!("Failed to extend variable storage with values: {e}",));
}
}
#[allow(missing_docs)]
#[derive(Debug)]
pub enum VariableStorageError {
InvalidVariableName { name: String },
VariableNotFound { name: String },
InternalError { error: Box<dyn Error + Send + Sync> },
}
impl Error for VariableStorageError {}
impl Display for VariableStorageError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use VariableStorageError::*;
match self {
InvalidVariableName { name } => write!(
f,
"{name} is not a valid variable name: Variable names must start with a \'$\'. (Did you mean to use \'${name}\'?)"
),
VariableNotFound { name } => write!(f, "Variable name {name} is not defined"),
InternalError { error } => write!(f, "Internal variable storage error: {error}"),
}
}
}
impl Clone for Box<dyn VariableStorage> {
fn clone(&self) -> Self {
self.clone_shallow()
}
}
#[derive(Debug, Clone, Default)]
pub struct MemoryVariableStorage(Arc<RwLock<HashMap<String, YarnValue>>>);
impl MemoryVariableStorage {
pub fn new() -> Self {
Self::default()
}
}
impl VariableStorage for MemoryVariableStorage {
fn clone_shallow(&self) -> Box<dyn VariableStorage> {
Box::new(self.clone())
}
fn set(&mut self, name: String, value: YarnValue) -> Result<()> {
Self::validate_name(&name)?;
self.0.write().unwrap().insert(name, value);
Ok(())
}
fn get(&self, name: &str) -> Result<YarnValue> {
Self::validate_name(name)?;
self.0.read().unwrap().get(name).cloned().ok_or_else(|| {
VariableStorageError::VariableNotFound {
name: name.to_string(),
}
})
}
fn extend(&mut self, values: HashMap<String, YarnValue>) -> Result<()> {
for name in values.keys() {
Self::validate_name(name)?;
}
self.0.write().unwrap().extend(values);
Ok(())
}
fn variables(&self) -> HashMap<String, YarnValue> {
self.0.read().unwrap().clone()
}
fn clear(&mut self) {
self.0.write().unwrap().clear();
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl MemoryVariableStorage {
fn validate_name(name: impl AsRef<str>) -> Result<()> {
let name = name.as_ref();
if name.starts_with('$') {
Ok(())
} else {
Err(VariableStorageError::InvalidVariableName {
name: name.to_string(),
})
}
}
}