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