Skip to main content

sim_lib_mutation/
vector.rs

1use std::sync::{Arc, RwLock};
2
3use sim_kernel::{Cx, Error, Expr, Object, ObjectCompat, Result, Value};
4
5use crate::standard_mutate_capability;
6
7/// A growable, index-addressed vector of [`Value`]s with in-place mutation.
8///
9/// Unlike the persistent sequences in `sim-lib-sequence`, writes here mutate the
10/// existing object in place; [`set`](MutableVector::set) and
11/// [`push`](MutableVector::push) require [`standard_mutate_capability`], while
12/// reads are always allowed. It renders to an [`Expr::Vector`] for inspection.
13#[sim_citizen_derive::non_citizen(
14    reason = "mutable vector handle; reconstruct from vector entries plus mutation policy",
15    kind = "handle",
16    descriptor = "core/Expr"
17)]
18pub struct MutableVector {
19    items: RwLock<Vec<Value>>,
20}
21
22impl MutableVector {
23    /// Create a vector seeded with `items`.
24    pub fn new(items: Vec<Value>) -> Self {
25        Self {
26            items: RwLock::new(items),
27        }
28    }
29
30    /// Return the current element count. Reads need no capability.
31    pub fn len(&self) -> Result<usize> {
32        Ok(self
33            .items
34            .read()
35            .map_err(|_| Error::PoisonedLock("mutation vector"))?
36            .len())
37    }
38
39    /// Return whether the vector currently has no elements.
40    pub fn is_empty(&self) -> Result<bool> {
41        Ok(self.len()? == 0)
42    }
43
44    /// Return a clone of the element at `index`, or `None` if out of bounds.
45    pub fn get(&self, index: usize) -> Result<Option<Value>> {
46        Ok(self
47            .items
48            .read()
49            .map_err(|_| Error::PoisonedLock("mutation vector"))?
50            .get(index)
51            .cloned())
52    }
53
54    /// Overwrite the element at `index`, requiring [`standard_mutate_capability`].
55    ///
56    /// Errors if `index` is out of bounds.
57    pub fn set(&self, cx: &mut Cx, index: usize, value: Value) -> Result<()> {
58        cx.require(&standard_mutate_capability())?;
59        let mut items = self
60            .items
61            .write()
62            .map_err(|_| Error::PoisonedLock("mutation vector"))?;
63        let Some(slot) = items.get_mut(index) else {
64            return Err(Error::Eval(format!(
65                "mutable vector index {index} out of bounds"
66            )));
67        };
68        *slot = value;
69        Ok(())
70    }
71
72    /// Append `value` to the end, requiring [`standard_mutate_capability`].
73    pub fn push(&self, cx: &mut Cx, value: Value) -> Result<()> {
74        cx.require(&standard_mutate_capability())?;
75        self.items
76            .write()
77            .map_err(|_| Error::PoisonedLock("mutation vector"))?
78            .push(value);
79        Ok(())
80    }
81
82    /// Return a snapshot clone of the current elements. Reads need no capability.
83    pub fn to_vec(&self) -> Result<Vec<Value>> {
84        Ok(self
85            .items
86            .read()
87            .map_err(|_| Error::PoisonedLock("mutation vector"))?
88            .clone())
89    }
90}
91
92impl Object for MutableVector {
93    fn display(&self, _cx: &mut Cx) -> Result<String> {
94        Ok(format!("#<mutation-vector {}>", self.len()?))
95    }
96
97    fn as_any(&self) -> &dyn std::any::Any {
98        self
99    }
100}
101
102impl ObjectCompat for MutableVector {
103    fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
104        Ok(Expr::Vector(
105            self.to_vec()?
106                .iter()
107                .map(|value| value.object().as_expr(cx))
108                .collect::<Result<Vec<_>>>()?,
109        ))
110    }
111}
112
113/// Construct a [`MutableVector`] from `items` and wrap it as a runtime [`Value`].
114pub fn mutable_vector(cx: &mut Cx, items: Vec<Value>) -> Result<Value> {
115    cx.factory().opaque(Arc::new(MutableVector::new(items)))
116}
117
118/// Build a [`MutableVector`] by copying the elements of a vector-shaped `value`.
119///
120/// The source is read through its [`Expr::Vector`] form, so the resulting
121/// mutable vector is an independent copy; errors if `value` is not vector-shaped.
122pub fn mutable_vector_from_value(cx: &mut Cx, value: &Value) -> Result<Value> {
123    let expr = value.object().as_expr(cx)?;
124    let Expr::Vector(items) = expr else {
125        return Err(Error::TypeMismatch {
126            expected: "vector expression",
127            found: "non-vector",
128        });
129    };
130    let mut values = Vec::with_capacity(items.len());
131    for item in items {
132        values.push(cx.factory().expr(item)?);
133    }
134    mutable_vector(cx, values)
135}
136
137/// Borrow the [`MutableVector`] behind `value`, or error if it is not one.
138pub fn mutable_vector_value(value: &Value) -> Result<&MutableVector> {
139    value
140        .object()
141        .downcast_ref::<MutableVector>()
142        .ok_or(Error::TypeMismatch {
143            expected: "mutable vector",
144            found: "non-vector",
145        })
146}