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