use std::sync::Arc;
use anyhow::{Result, anyhow, bail};
use parking_lot::Mutex;
use super::value::{CellKind, Value, ValueRef};
pub(super) fn make_cell(kind: CellKind, inner: Value) -> Value {
Value::Cell(kind, Arc::new(Mutex::new(inner)))
}
pub(super) fn cell_method(
kind: CellKind,
slot: &Arc<Mutex<Value>>,
name: &str,
args: &mut [Value],
) -> Result<Option<Value>> {
if kind.is_shared_pointer() && interior_method(name) {
let inner = slot.lock().clone();
let Value::Cell(inner_kind, inner_slot) = inner else {
bail!("no method `{name}` on {}", kind_name(kind));
};
return cell_method(inner_kind, &inner_slot, name, args);
}
Ok(Some(match name {
"clone" => Value::Cell(kind, slot.clone()),
"borrow" => {
require(kind, CellKind::RefCell, name)?;
slot.lock().clone()
}
"borrow_mut" => {
require(kind, CellKind::RefCell, name)?;
Value::Ref(Arc::new(ValueRef::cell_slot(slot.clone())))
}
"lock" | "try_lock" | "blocking_lock" => {
if kind == CellKind::TokioMutex {
let guard = Value::Ref(Arc::new(ValueRef::cell_slot(slot.clone())));
if name == "try_lock" {
Value::ok(guard)
} else {
guard
}
} else {
require(kind, CellKind::Mutex, name)?;
if name == "blocking_lock" {
bail!("no method `blocking_lock` on std Mutex");
}
Value::ok(Value::Ref(Arc::new(ValueRef::cell_slot(slot.clone()))))
}
}
"get" if kind == CellKind::Cell => slot.lock().clone(),
"set" => {
require_interior(kind, name)?;
let new = args.first().cloned().unwrap_or(Value::Unit);
*slot.lock() = new;
Value::Unit
}
"replace" => {
require_interior(kind, name)?;
let new = args.first().cloned().unwrap_or(Value::Unit);
let mut guard = slot.lock();
let old = guard.clone();
*guard = new;
old
}
"take" => {
require_interior(kind, name)?;
let mut guard = slot.lock();
let old = guard.clone();
*guard = old.default_like();
old
}
"get_mut" => {
require_interior(kind, name)?;
Value::Ref(Arc::new(ValueRef::cell_slot(slot.clone())))
}
"into_inner" => slot.lock().clone(),
_ => return Ok(None),
}))
}
fn interior_method(name: &str) -> bool {
matches!(
name,
"borrow"
| "borrow_mut"
| "lock"
| "try_lock"
| "blocking_lock"
| "get"
| "set"
| "replace"
| "take"
| "get_mut"
)
}
fn kind_name(kind: CellKind) -> &'static str {
match kind {
CellKind::Rc => "Rc",
CellKind::Arc => "Arc",
CellKind::RefCell => "RefCell",
CellKind::Cell => "Cell",
CellKind::Mutex | CellKind::TokioMutex => "Mutex",
}
}
fn require(kind: CellKind, wanted: CellKind, name: &str) -> Result<()> {
if kind == wanted {
Ok(())
} else {
Err(anyhow!("no method `{name}` on {}", kind_name(kind)))
}
}
fn require_interior(kind: CellKind, name: &str) -> Result<()> {
if kind.is_shared_pointer() {
Err(anyhow!("no method `{name}` on {}", kind_name(kind)))
} else {
Ok(())
}
}