use std::sync::{Arc, Mutex};
use sim_kernel::{Error, Result, Symbol, Value};
#[derive(Clone, Debug)]
pub struct BindingCell {
name: Symbol,
slot: Arc<Mutex<BindingCellState>>,
}
#[derive(Clone, Debug)]
pub enum BindingCellState {
Uninitialized,
Initialized(Value),
Deleted,
Immutable(Value),
LiveAlias(BindingCell),
}
impl BindingCell {
pub(crate) fn from_slot(name: Symbol, slot: Arc<Mutex<BindingCellState>>) -> Self {
Self { name, slot }
}
pub fn uninitialized(name: Symbol) -> Self {
Self::from_slot(name, Arc::new(Mutex::new(BindingCellState::Uninitialized)))
}
pub fn initialized(name: Symbol, value: Value) -> Self {
Self::from_slot(
name,
Arc::new(Mutex::new(BindingCellState::Initialized(value))),
)
}
pub fn immutable(name: Symbol, value: Value) -> Self {
Self::from_slot(
name,
Arc::new(Mutex::new(BindingCellState::Immutable(value))),
)
}
pub fn live_alias(name: Symbol, target: BindingCell) -> Self {
Self::from_slot(
name,
Arc::new(Mutex::new(BindingCellState::LiveAlias(target))),
)
}
pub fn name(&self) -> &Symbol {
&self.name
}
pub fn get(&self) -> Result<Value> {
let state = self
.slot
.lock()
.map_err(|_| Error::Eval(format!("binding cell {} lock is poisoned", self.name)))?
.clone();
match state {
BindingCellState::Initialized(value) | BindingCellState::Immutable(value) => Ok(value),
BindingCellState::LiveAlias(target) => target.get(),
BindingCellState::Uninitialized => Err(Error::Eval(format!(
"binding cell {} is not initialized",
self.name
))),
BindingCellState::Deleted => Err(Error::Eval(format!(
"binding cell {} is deleted",
self.name
))),
}
}
pub fn set(&self, value: Value) -> Result<()> {
let mut state = self
.slot
.lock()
.map_err(|_| Error::Eval(format!("binding cell {} lock is poisoned", self.name)))?;
match &mut *state {
BindingCellState::LiveAlias(target) => target.set(value),
BindingCellState::Immutable(_) => Err(Error::Eval(format!(
"binding cell {} is immutable",
self.name
))),
BindingCellState::Deleted => Err(Error::Eval(format!(
"binding cell {} is deleted",
self.name
))),
BindingCellState::Uninitialized | BindingCellState::Initialized(_) => {
*state = BindingCellState::Initialized(value);
Ok(())
}
}
}
pub fn delete(&self) -> Result<()> {
let mut state = self
.slot
.lock()
.map_err(|_| Error::Eval(format!("binding cell {} lock is poisoned", self.name)))?;
if matches!(*state, BindingCellState::Immutable(_)) {
return Err(Error::Eval(format!(
"binding cell {} is immutable",
self.name
)));
}
*state = BindingCellState::Deleted;
Ok(())
}
pub fn state(&self) -> Result<BindingCellState> {
self.slot
.lock()
.map_err(|_| Error::Eval(format!("binding cell {} lock is poisoned", self.name)))
.map(|state| state.clone())
}
}