1use crate::dif::update::{DIFProperty, DIFUpdateData};
2use crate::dif::value::DIFValueContainer;
3use crate::runtime::memory::Memory;
4use crate::{
5 references::reference::{AccessError, Reference},
6 values::{core_value::CoreValue, value_container::ValueContainer},
7};
8use std::cell::RefCell;
9use crate::references::observers::TransceiverId;
10
11impl Reference {
12
13
14 fn handle_update(&self, source_id: TransceiverId, handler: impl FnOnce() -> Result<DIFUpdateData, AccessError>) -> Result<(), AccessError> {
20 if !self.is_mutable() {
21 return Err(AccessError::ImmutableReference);
22 }
23 let update_data = handler()?;
24 self.notify_observers(&update_data.with_source(source_id));
25 Ok(())
26 }
27
28 pub fn try_set_property(
30 &self,
31 source_id: TransceiverId,
32 key: ValueContainer,
33 val: ValueContainer,
34 memory: &RefCell<Memory>,
35 ) -> Result<(), AccessError> {
36 self.handle_update(source_id, move || {
37 let val = val.upgrade_combined_value_to_reference();
38 self.with_value_unchecked(|value| {
39 match value.inner {
40 CoreValue::Map(ref mut map) => {
41 map.try_set(key.clone(), val.clone())?;
43 }
44 _ => {
45 return Err(AccessError::InvalidOperation(format!(
47 "Cannot set property '{}' on non-map value: {:?}",
48 key, value
49 )));
50 }
51 }
52 Ok(DIFUpdateData::set(
53 DIFValueContainer::from_value_container(&key, memory),
54 DIFValueContainer::from_value_container(&val, memory)
55 ))
56 })
57 })
58 }
59
60 pub fn try_set_text_property(
62 &self,
63 source_id: TransceiverId,
64 key: &str,
65 val: ValueContainer,
66 memory: &RefCell<Memory>,
67 ) -> Result<(), AccessError> {
68 self.handle_update(source_id, move || {
69 let val = val.upgrade_combined_value_to_reference();
71 self.with_value_unchecked(|value| {
72 match value.inner {
73 CoreValue::Map(ref mut map) => {
74 map.try_set(key, val.clone())?;
76 }
77 _ => {
78 return Err(AccessError::InvalidOperation(format!(
80 "Cannot set property '{}' on non-map value: {:?}",
81 key, value
82 )));
83 }
84 }
85 Ok(DIFUpdateData::set(
86 key, DIFValueContainer::from_value_container(&val, memory)
87 ))
88 })
89 })
90 }
91
92 pub fn try_set_numeric_property(
93 &self,
94 source_id: TransceiverId,
95 index: u32,
96 val: ValueContainer,
97 memory: &RefCell<Memory>,
98 ) -> Result<(), AccessError> {
99 self.handle_update(source_id, move || {
100 let val = val.upgrade_combined_value_to_reference();
101 self.with_value_unchecked(|value| {
102 match value.inner {
103 CoreValue::List(ref mut list) => {
104 list.set(index, self.bind_child(val.clone())).ok_or({
105 AccessError::IndexOutOfBounds(index)
106 })?;
107 }
108 CoreValue::Text(ref mut text) => {
109 if let ValueContainer::Value(v) = &val {
110 if let CoreValue::Text(new_char) = &v.inner && new_char.0.len() == 1 {
111 let char = new_char.0.chars().next().unwrap_or('\0');
112 text.set_char_at(index as usize, char).map_err(| _| AccessError::IndexOutOfBounds(index))?;
113 } else {
114 return Err(AccessError::InvalidOperation(
115 "Can only set char character in text".to_string(),
116 ));
117 }
118 } else {
119 return Err(AccessError::CanNotUseReferenceAsKey);
120 }
121 }
122 _ => {
123 return Err(AccessError::InvalidOperation(format!(
124 "Cannot set numeric property '{}' on non-list/text value: {:?}",
125 index, value
126 )));
127 }
128 }
129
130 Ok(DIFUpdateData::set(
131 DIFProperty::Index(index as i64),
132 DIFValueContainer::from_value_container(&val, memory),
133 ))
134 })
135 })
136 }
137
138 pub fn try_set_value<T: Into<ValueContainer>>(
140 &self,
141 source_id: TransceiverId,
142 value: T,
143 memory: &RefCell<Memory>,
144 ) -> Result<(), AccessError> {
145 self.handle_update(source_id, move || {
146 let value_container = &value.into();
148 self.with_value_unchecked(|core_value| {
149 core_value.inner =
151 value_container.to_value().borrow().inner.clone();
152 Ok(DIFUpdateData::replace(
153 DIFValueContainer::from_value_container(value_container, memory),
154 ))
155 })
156 })
157 }
158
159 pub fn try_push_value<T: Into<ValueContainer>>(
161 &self,
162 source_id: TransceiverId,
164 value: T,
165 memory: &RefCell<Memory>,
166 ) -> Result<(), AccessError> {
167 self.handle_update(source_id, move || {
168 let value_container =
169 value.into().upgrade_combined_value_to_reference();
170 self.with_value_unchecked(move |core_value| {
171 match &mut core_value.inner {
172 CoreValue::List(list) => {
173 list.push(value_container.clone());
175 }
176 _ => {
177 return Err(AccessError::InvalidOperation(format!(
178 "Cannot push value to non-list value: {:?}",
179 core_value
180 )));
181 }
182 }
183 Ok(DIFUpdateData::push(DIFValueContainer::from_value_container(&value_container, memory)))
184 })
185 })
186 }
187
188 pub fn try_delete_property(
191 &self,
192 source_id: TransceiverId,
193 key: ValueContainer,
194 memory: &RefCell<Memory>,
195 ) -> Result<(), AccessError> {
196 self.handle_update(source_id, move || {
197 let key = key.upgrade_combined_value_to_reference();
198 self.with_value_unchecked(|value| {
199 match value.inner {
200 CoreValue::Map(ref mut map) => {
201 map.remove(&key)?;
202 }
203 _ => {
204 return Err(AccessError::InvalidOperation(format!(
205 "Cannot delete property '{:?}' on non-map value: {:?}",
206 key, value
207 )));
208 }
209 }
210 Ok(DIFUpdateData::remove(DIFValueContainer::from_value_container(&key, memory)))
211 })
212 })
213 }
214
215 pub fn try_clear(&self, source_id: TransceiverId) -> Result<(), AccessError> {
216 self.handle_update(source_id, move || {
217 self.with_value_unchecked(|value| {
218 match value.inner {
219 CoreValue::Map(ref mut map) => {
220 map.clear()?;
221 }
222 _ => {
223 return Err(AccessError::InvalidOperation(format!(
224 "Cannot clear non-list/map value: {:?}",
225 value
226 )));
227 }
228 }
229 Ok(DIFUpdateData::clear())
230 })
231 })
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use crate::references::reference::{
238 AccessError, AssignmentError, ReferenceMutability,
239 };
240 use crate::runtime::memory::Memory;
241 use crate::values::core_values::list::List;
242 use crate::values::core_values::map::Map;
243 use crate::{
244 references::reference::Reference,
245 values::value_container::ValueContainer,
246 };
247 use std::assert_matches::assert_matches;
248 use std::cell::RefCell;
249
250 #[test]
251 fn push() {
252 let memory = &RefCell::new(Memory::default());
253 let list = vec![
254 ValueContainer::from(1),
255 ValueContainer::from(2),
256 ValueContainer::from(3),
257 ];
258 let list_ref =
259 Reference::try_mut_from(List::from(list).into()).unwrap();
260 list_ref
261 .try_push_value(0, ValueContainer::from(4), memory)
262 .expect("Failed to push value to list");
263 let updated_value = list_ref.get_numeric_property(3).unwrap();
264 assert_eq!(updated_value, ValueContainer::from(4));
265
266 let int_ref = Reference::from(List::from(vec![ValueContainer::from(42)]));
268 let result = int_ref.try_push_value(0, ValueContainer::from(99), memory);
269 assert_matches!(result, Err(AccessError::ImmutableReference));
270
271 let int_ref = Reference::try_mut_from(42.into()).unwrap();
273 let result = int_ref.try_push_value(0, ValueContainer::from(99), memory);
274 assert_matches!(result, Err(AccessError::InvalidOperation(_)));
275 }
276
277 #[test]
278 fn property() {
279 let memory = &RefCell::new(Memory::default());
280
281 let map = Map::from(vec![
282 ("key1".to_string(), ValueContainer::from(1)),
283 ("key2".to_string(), ValueContainer::from(2)),
284 ]);
285 let map_ref =
286 Reference::try_mut_from(ValueContainer::from(map)).unwrap();
287 map_ref
289 .try_set_property(0, "key1".into(), ValueContainer::from(42), memory)
290 .expect("Failed to set existing property");
291 let updated_value = map_ref
292 .try_get_property(ValueContainer::from("key1"))
293 .unwrap();
294 assert_eq!(updated_value, 42.into());
295
296 let result = map_ref.try_set_property(
298 0,
299 "new".into(),
300 ValueContainer::from(99),
301 memory,
302 );
303 assert!(result.is_ok());
304 let new_value = map_ref
305 .try_get_property(ValueContainer::from("new"))
306 .unwrap();
307 assert_eq!(new_value, 99.into());
308 }
309
310 #[test]
311 fn numeric_property() {
312 let memory = &RefCell::new(Memory::default());
313
314 let list = vec![
315 ValueContainer::from(1),
316 ValueContainer::from(2),
317 ValueContainer::from(3),
318 ];
319 let list_ref =
320 Reference::try_mut_from(ValueContainer::from(list)).unwrap();
321
322 list_ref
324 .try_set_numeric_property(0, 1, ValueContainer::from(42), memory)
325 .expect("Failed to set existing index");
326 let updated_value = list_ref.get_numeric_property(1).unwrap();
327 assert_eq!(updated_value, ValueContainer::from(42));
328
329 let result = list_ref.try_set_numeric_property(
331 0,
332 5,
333 ValueContainer::from(99),
334 memory,
335 );
336 assert_matches!(result, Err(AccessError::IndexOutOfBounds(5)));
337
338 let int_ref = Reference::try_mut_from(42.into()).unwrap();
340 let result = int_ref.try_set_numeric_property(
341 0,
342 0,
343 ValueContainer::from(99),
344 memory,
345 );
346 assert_matches!(result, Err(AccessError::InvalidOperation(_)));
347 }
348
349 #[test]
350 fn text_property() {
351 let memory = &RefCell::new(Memory::default());
352
353 let struct_val = Map::from(vec![
354 (ValueContainer::from("name"), ValueContainer::from("Alice")),
355 (ValueContainer::from("age"), ValueContainer::from(30)),
356 ]);
357 let struct_ref =
358 Reference::try_mut_from(ValueContainer::from(struct_val)).unwrap();
359
360 struct_ref
362 .try_set_text_property(0, "name", ValueContainer::from("Bob"), memory)
363 .expect("Failed to set existing property");
364 let name = struct_ref.try_get_text_property("name").unwrap();
365 assert_eq!(name, "Bob".into());
366
367 let result = struct_ref.try_set_text_property(
369 0,
370 "nonexistent",
371 ValueContainer::from("Value"),
372 memory,
373 );
374 assert_matches!(result, Ok(()));
375
376 let int_ref = Reference::try_mut_from(42.into()).unwrap();
378 let result = int_ref.try_set_text_property(
379 0,
380 "name",
381 ValueContainer::from("Bob"),
382 memory,
383 );
384 assert_matches!(result, Err(AccessError::InvalidOperation(_)));
385 }
386
387 #[test]
388 fn immutable_reference_fails() {
389 let memory = &RefCell::new(Memory::default());
390
391 let r = Reference::from(42);
392 assert_matches!(
393 r.try_set_value(0, 43, memory),
394 Err(AccessError::ImmutableReference)
395 );
396
397 let r = Reference::try_new_from_value_container(
398 42.into(),
399 None,
400 None,
401 ReferenceMutability::Final,
402 )
403 .unwrap();
404 assert_matches!(
405 r.try_set_value(0, 43, memory),
406 Err(AccessError::ImmutableReference)
407 );
408
409 let r = Reference::try_new_from_value_container(
410 42.into(),
411 None,
412 None,
413 ReferenceMutability::Immutable,
414 )
415 .unwrap();
416 assert_matches!(
417 r.try_set_value(0, 43, memory),
418 Err(AccessError::ImmutableReference)
419 );
420 }
421}