use alloc::{
collections::btree_map::BTreeMap,
string::{String, ToString},
};
use thiserror::Error;
use crate::{ConstString, RwLock, scripting_value::ScriptingValue};
pub trait Environment: Send + Sync {
fn define_env(&mut self, key: &str, value: impl Into<ScriptingValue>) -> Result<(), Error>;
fn get_env(&self, key: &str) -> Result<ScriptingValue, Error>;
fn set_env(&mut self, key: &str, value: impl Into<ScriptingValue>) -> Result<(), Error>;
}
#[derive(Error, Debug)]
pub enum Error {
#[error("the environment variable {name} exceeds the limits of its defined type")]
EnvVarExceedsLimits {
name: ConstString,
},
#[error("the environment variable {name} has not been defined")]
EnvVarNotDefined {
name: ConstString,
},
#[error("the environment variable {name} has an unknown type")]
EnvVarUnknownType {
name: ConstString,
},
#[error("the type of the environment variable {name} does not match its former definition")]
EnvVarWrongType {
name: ConstString,
},
#[error("setting environment variable {name} failed: {cause}")]
EnvVarSet {
name: ConstString,
cause: ConstString,
},
#[error("cast of variable {name} to {var_type} failed")]
EnvVarTypeCast {
name: ConstString,
var_type: ConstString,
},
}
#[derive(Debug, Default)]
pub struct DefaultEnvironment {
storage: RwLock<BTreeMap<String, ScriptingValue>>,
}
impl Environment for DefaultEnvironment {
fn define_env(&mut self, key: &str, value: impl Into<ScriptingValue>) -> Result<(), Error> {
self.storage
.write()
.insert(key.to_string(), value.into());
Ok(())
}
fn get_env(&self, key: &str) -> Result<ScriptingValue, Error> {
self.storage.read().get(key).map_or_else(
|| Err(Error::EnvVarNotDefined { name: key.into() }),
|value| Ok(value.clone()),
)
}
fn set_env(&mut self, key: &str, value: impl Into<ScriptingValue>) -> Result<(), Error> {
if self.storage.read().contains_key(key) {
self.storage
.write()
.insert(key.to_string(), value.into());
Ok(())
} else {
Err(Error::EnvVarNotDefined { name: key.into() })
}
}
}