1use crate::{
2 dif::{
3 update::{DIFKey, DIFUpdateData},
4 value::DIFValueContainer,
5 },
6 runtime::memory::Memory,
7 shared_values::{
8 observers::TransceiverId,
9 shared_container::{AccessError, SharedContainer},
10 },
11 values::{
12 core_value::CoreValue,
13 value_container::{ValueContainer, ValueKey},
14 },
15};
16use core::{cell::RefCell, ops::FnOnce, prelude::rust_2024::*};
17
18use crate::prelude::*;
19pub enum DIFUpdateDataOrMemory<'a> {
20 Update(&'a DIFUpdateData),
21 Memory(&'a RefCell<Memory>),
22}
23
24impl<'a> From<&'a DIFUpdateData> for DIFUpdateDataOrMemory<'a> {
25 fn from(update: &'a DIFUpdateData) -> Self {
26 DIFUpdateDataOrMemory::Update(update)
27 }
28}
29
30impl<'a> From<&'a RefCell<Memory>> for DIFUpdateDataOrMemory<'a> {
31 fn from(memory: &'a RefCell<Memory>) -> Self {
32 DIFUpdateDataOrMemory::Memory(memory)
33 }
34}
35
36impl SharedContainer {
37 fn handle_update<'a>(
43 &self,
44 _source_id: TransceiverId,
45 handler: impl FnOnce() -> Result<&'a DIFUpdateData, AccessError>,
46 ) -> Result<(), AccessError> {
47 if !self.is_mutable() {
48 return Err(AccessError::ImmutableReference);
49 }
50 let _update_data = handler()?;
51 Ok(())
53 }
54
55 fn assert_mutable(&self) -> Result<(), AccessError> {
56 if !self.is_mutable() {
57 return Err(AccessError::ImmutableReference);
58 }
59 Ok(())
60 }
61
62 pub fn try_set_property<'a>(
64 &self,
65 source_id: TransceiverId,
66 maybe_dif_update_data: Option<&DIFUpdateData>,
67 key: impl Into<ValueKey<'a>>,
68 val: ValueContainer,
69 ) -> Result<(), AccessError> {
70 self.assert_mutable()?;
71
72 let key = key.into();
73
74 let dif_update = match maybe_dif_update_data {
75 Some(update) => update,
76 None => &DIFUpdateData::set(
77 DIFKey::from_value_key(&key),
78 DIFValueContainer::from_value_container(&val),
79 ),
80 };
81
82 self.with_value_unchecked(|value| {
83 value.try_set_property(key, val.clone())
84 })?;
85
86 self.notify_observers(&dif_update.with_source(source_id));
87 Ok(())
88 }
89
90 pub fn try_replace(
92 &self,
93 source_id: TransceiverId,
94 maybe_dif_update_data: Option<&DIFUpdateData>,
95 value: impl Into<ValueContainer>,
96 ) -> Result<(), AccessError> {
97 self.assert_mutable()?;
98
99 let value_container = &value.into();
101
102 let dif_update = match maybe_dif_update_data {
103 Some(update) => update,
104 None => &DIFUpdateData::replace(
105 DIFValueContainer::from_value_container(value_container),
106 ),
107 };
108
109 self.with_value_unchecked(|core_value| {
110 core_value.inner =
112 value_container.to_value().borrow().inner.clone();
113 });
114
115 self.notify_observers(&dif_update.with_source(source_id));
116 Ok(())
117 }
118
119 pub fn try_append_value<'a>(
121 &self,
122 source_id: TransceiverId,
123 maybe_dif_update_data: Option<&DIFUpdateData>,
124 value: impl Into<ValueContainer>,
125 ) -> Result<(), AccessError> {
126 self.assert_mutable()?;
127 let value_container = value.into();
128
129 let dif_update = match maybe_dif_update_data {
130 Some(update) => update,
131 None => &DIFUpdateData::append(
132 DIFValueContainer::from_value_container(&value_container),
133 ),
134 };
135
136 self.with_value_unchecked(move |core_value| {
137 match &mut core_value.inner {
138 CoreValue::List(list) => {
139 list.push(value_container);
140 }
141 _ => {
142 return Err(AccessError::InvalidOperation(format!(
143 "Cannot push value to non-list value: {:?}",
144 core_value
145 )));
146 }
147 }
148
149 Ok(())
150 })?;
151
152 self.notify_observers(&dif_update.with_source(source_id));
153 Ok(())
154 }
155
156 pub fn try_delete_property<'a>(
159 &self,
160 source_id: TransceiverId,
161 maybe_dif_update_data: Option<&DIFUpdateData>,
162 key: impl Into<ValueKey<'a>>,
163 ) -> Result<(), AccessError> {
164 self.assert_mutable()?;
165 let key = key.into();
166
167 let dif_update = match maybe_dif_update_data {
168 Some(update) => update,
169 None => &DIFUpdateData::delete(DIFKey::from_value_key(&key)),
170 };
171
172 self.with_value_unchecked(|value| {
173 match value.inner {
174 CoreValue::Map(ref mut map) => {
175 key.with_value_container(|key| map.delete(key))?;
176 }
177 CoreValue::List(ref mut list) => {
178 if let Some(index) = key.try_as_index() {
179 list.delete(index).map_err(|err| {
180 AccessError::IndexOutOfBounds(err)
181 })?;
182 } else {
183 return Err(AccessError::InvalidIndexKey);
184 }
185 }
186 _ => {
187 return Err(AccessError::InvalidOperation(format!(
188 "Cannot delete property '{:?}' on non-map value: {:?}",
189 key, value
190 )));
191 }
192 }
193
194 Ok(())
195 })?;
196
197 self.notify_observers(&dif_update.with_source(source_id));
198 Ok(())
199 }
200
201 pub fn try_clear(
202 &self,
203 source_id: TransceiverId,
204 ) -> Result<(), AccessError> {
205 self.assert_mutable()?;
206
207 self.with_value_unchecked(|value| {
208 match value.inner {
209 CoreValue::Map(ref mut map) => {
210 map.clear()?;
211 }
212 CoreValue::List(ref mut list) => {
213 list.clear();
214 }
215 _ => {
216 return Err(AccessError::InvalidOperation(format!(
217 "Cannot clear non-list/map value: {:?}",
218 value
219 )));
220 }
221 }
222
223 Ok(())
224 })?;
225
226 self.notify_observers(&DIFUpdateData::clear().with_source(source_id));
227 Ok(())
228 }
229
230 pub fn try_list_splice<'a>(
231 &self,
232 source_id: TransceiverId,
233 maybe_dif_update_data: Option<&DIFUpdateData>,
234 range: core::ops::Range<u32>,
235 items: Vec<ValueContainer>,
236 ) -> Result<(), AccessError> {
237 self.assert_mutable()?;
238
239 let dif_update = match maybe_dif_update_data {
240 Some(update) => update,
241 None => &DIFUpdateData::list_splice(
242 range.clone(),
243 items
244 .iter()
245 .map(DIFValueContainer::from_value_container)
246 .collect(),
247 ),
248 };
249
250 self.with_value_unchecked(|value| {
251 match value.inner {
252 CoreValue::List(ref mut list) => {
253 list.splice(range, items);
254 }
255 _ => {
256 return Err(AccessError::InvalidOperation(format!(
257 "Cannot apply splice operation on non-list value: {:?}",
258 value
259 )));
260 }
261 }
262
263 Ok(())
264 })?;
265
266 self.notify_observers(&dif_update.with_source(source_id));
267 Ok(())
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use crate::{
274 prelude::*,
275 runtime::memory::Memory,
276 shared_values::{
277 pointer::Pointer,
278 shared_container::{
279 AccessError, IndexOutOfBoundsError, SharedContainer,
280 SharedContainerMutability,
281 },
282 },
283 values::{
284 core_values::{list::List, map::Map},
285 value_container::ValueContainer,
286 },
287 };
288 use core::{assert_matches, cell::RefCell};
289
290 #[test]
291 fn push() {
292 let list = vec![
293 ValueContainer::from(1),
294 ValueContainer::from(2),
295 ValueContainer::from(3),
296 ];
297 let list_ref =
298 SharedContainer::boxed_mut(List::from(list).into(), Pointer::NULL)
299 .unwrap();
300 list_ref
301 .try_append_value(0, None, ValueContainer::from(4))
302 .expect("Failed to push value to list");
303 let updated_value = list_ref.try_get_property(3).unwrap();
304 assert_eq!(updated_value, ValueContainer::from(4));
305
306 let int_ref = SharedContainer::boxed(
308 List::from(vec![ValueContainer::from(42)]),
309 Pointer::NULL,
310 );
311 let result =
312 int_ref.try_append_value(0, None, ValueContainer::from(99));
313 assert_matches!(result, Err(AccessError::ImmutableReference));
314
315 let int_ref =
317 SharedContainer::boxed_mut(42.into(), Pointer::NULL).unwrap();
318 let result =
319 int_ref.try_append_value(0, None, ValueContainer::from(99));
320 assert_matches!(result, Err(AccessError::InvalidOperation(_)));
321 }
322
323 #[test]
324 fn property() {
325 let map = Map::from(vec![
326 ("key1".to_string(), ValueContainer::from(1)),
327 ("key2".to_string(), ValueContainer::from(2)),
328 ]);
329 let map_ref = SharedContainer::boxed_mut(
330 ValueContainer::from(map),
331 Pointer::NULL,
332 )
333 .unwrap();
334 map_ref
336 .try_set_property(0, None, "key1", ValueContainer::from(42))
337 .expect("Failed to set existing property");
338 let updated_value = map_ref.try_get_property("key1").unwrap();
339 assert_eq!(updated_value, 42.into());
340
341 let result =
343 map_ref.try_set_property(0, None, "new", ValueContainer::from(99));
344 assert!(result.is_ok());
345 let new_value = map_ref.try_get_property("new").unwrap();
346 assert_eq!(new_value, 99.into());
347 }
348
349 #[test]
350 fn numeric_property() {
351 let list = vec![
352 ValueContainer::from(1),
353 ValueContainer::from(2),
354 ValueContainer::from(3),
355 ];
356 let list_ref = SharedContainer::boxed_mut(
357 ValueContainer::from(list),
358 Pointer::NULL,
359 )
360 .unwrap();
361
362 list_ref
364 .try_set_property(0, None, 1, ValueContainer::from(42))
365 .expect("Failed to set existing index");
366 let updated_value = list_ref.try_get_property(1).unwrap();
367 assert_eq!(updated_value, ValueContainer::from(42));
368
369 let result =
371 list_ref.try_set_property(0, None, 5, ValueContainer::from(99));
372 assert_matches!(
373 result,
374 Err(AccessError::IndexOutOfBounds(IndexOutOfBoundsError {
375 index: 5
376 }))
377 );
378
379 let int_ref =
381 SharedContainer::boxed_mut(42.into(), Pointer::NULL).unwrap();
382 let result =
383 int_ref.try_set_property(0, None, 0, ValueContainer::from(99));
384 assert_matches!(result, Err(AccessError::InvalidOperation(_)));
385 }
386
387 #[test]
388 fn text_property() {
389 let struct_val = Map::from(vec![
390 (ValueContainer::from("name"), ValueContainer::from("Alice")),
391 (ValueContainer::from("age"), ValueContainer::from(30)),
392 ]);
393 let struct_ref = SharedContainer::boxed_mut(
394 ValueContainer::from(struct_val),
395 Pointer::NULL,
396 )
397 .unwrap();
398
399 struct_ref
401 .try_set_property(0, None, "name", ValueContainer::from("Bob"))
402 .expect("Failed to set existing property");
403 let name = struct_ref.try_get_property("name").unwrap();
404 assert_eq!(name, "Bob".into());
405
406 let result = struct_ref.try_set_property(
408 0,
409 None,
410 "nonexistent",
411 ValueContainer::from("Value"),
412 );
413 assert_matches!(result, Ok(()));
414
415 let int_ref =
417 SharedContainer::boxed_mut(42.into(), Pointer::NULL).unwrap();
418 let result = int_ref.try_set_property(
419 0,
420 None,
421 "name",
422 ValueContainer::from("Bob"),
423 );
424 assert_matches!(result, Err(AccessError::InvalidOperation(_)));
425 }
426
427 #[test]
428 fn immutable_reference_fails() {
429 let r = SharedContainer::boxed(42, Pointer::NULL);
430 assert_matches!(
431 r.try_replace(0, None, 43),
432 Err(AccessError::ImmutableReference)
433 );
434
435 let r = SharedContainer::try_boxed(
436 42.into(),
437 None,
438 Pointer::NULL,
439 SharedContainerMutability::Immutable,
440 )
441 .unwrap();
442 assert_matches!(
443 r.try_replace(0, None, 43),
444 Err(AccessError::ImmutableReference)
445 );
446 }
447}