datex_core/runtime/
dif_interface.rs

1use crate::dif::interface::DIFResolveReferenceError;
2use crate::dif::reference::DIFReference;
3use crate::dif::r#type::DIFTypeContainer;
4use crate::dif::update::{DIFProperty, DIFUpdateData};
5use crate::references::observers::{ObserveOptions, Observer, TransceiverId};
6use crate::references::reference::{AccessError, ReferenceMutability};
7use crate::runtime::RuntimeInternal;
8use crate::values::value_container::ValueContainer;
9use crate::{
10    dif::{
11        interface::{
12            DIFApplyError, DIFCreatePointerError, DIFInterface,
13            DIFObserveError, DIFUpdateError,
14        },
15        value::DIFValueContainer,
16    },
17    references::reference::Reference,
18    values::pointer::PointerAddress,
19};
20use datex_core::dif::update::DIFUpdate;
21use std::rc::Rc;
22
23impl RuntimeInternal {
24    fn resolve_in_memory_reference(
25        &self,
26        address: &PointerAddress,
27    ) -> Option<Reference> {
28        self.memory.borrow().get_reference(address).cloned()
29    }
30    // FIXME #398 implement async resolution
31    async fn resolve_reference(
32        &self,
33        address: &PointerAddress,
34    ) -> Option<Reference> {
35        self.memory.borrow().get_reference(address).cloned()
36    }
37}
38
39impl DIFInterface for RuntimeInternal {
40    fn update(
41        &self,
42        source_id: TransceiverId,
43        address: PointerAddress,
44        update: DIFUpdateData,
45    ) -> Result<(), DIFUpdateError> {
46        let reference = self
47            .resolve_in_memory_reference(&address)
48            .ok_or(DIFUpdateError::ReferenceNotFound)?;
49        match update {
50            DIFUpdateData::Set { key, value } => {
51                if !reference.supports_property_access() {
52                    return Err(DIFUpdateError::AccessError(
53                        AccessError::InvalidOperation(
54                            "Reference does not support property access"
55                                .to_string(),
56                        ),
57                    ));
58                }
59                let value_container = value.to_value_container(&self.memory)?;
60                match key {
61                    DIFProperty::Text(key) => reference.try_set_text_property(
62                        source_id,
63                        &key,
64                        value_container,
65                        &self.memory,
66                    )?,
67                    DIFProperty::Index(key) => reference
68                        .try_set_numeric_property(
69                            source_id,
70                            key as u32,
71                            value_container,
72                            &self.memory,
73                        )?,
74                    DIFProperty::Value(key) => {
75                        let key = key.to_value_container(&self.memory)?;
76                        reference.try_set_property(
77                            source_id,
78                            key,
79                            value_container,
80                            &self.memory,
81                        )?
82                    }
83                }
84            }
85            DIFUpdateData::Replace { value } => reference.try_set_value(
86                source_id,
87                value.to_value_container(&self.memory)?,
88                &self.memory,
89            )?,
90            DIFUpdateData::Push { value } => {
91                if !reference.supports_push() {
92                    return Err(DIFUpdateError::AccessError(
93                        AccessError::InvalidOperation(
94                            "Reference does not support push operation"
95                                .to_string(),
96                        ),
97                    ));
98                }
99                reference.try_push_value(
100                    source_id,
101                    value.to_value_container(&self.memory)?,
102                    &self.memory,
103                )?
104            }
105            DIFUpdateData::Clear => {
106                if !reference.supports_clear() {
107                    return Err(DIFUpdateError::AccessError(
108                        AccessError::InvalidOperation(
109                            "Reference does not support clear operation"
110                                .to_string(),
111                        ),
112                    ));
113                }
114                reference.try_clear(source_id)?
115            }
116            DIFUpdateData::Remove { key } => {
117                if !reference.supports_property_access() {
118                    return Err(DIFUpdateError::AccessError(
119                        AccessError::InvalidOperation(
120                            "Reference does not support property access"
121                                .to_string(),
122                        ),
123                    ));
124                }
125
126                match key {
127                    DIFProperty::Text(key) => reference.try_delete_property(
128                        source_id,
129                        ValueContainer::from(key),
130                        &self.memory,
131                    )?,
132                    DIFProperty::Index(key) => reference.try_delete_property(
133                        source_id,
134                        ValueContainer::from(key),
135                        &self.memory,
136                    )?,
137                    DIFProperty::Value(key) => {
138                        let key = key.to_value_container(&self.memory)?;
139                        reference.try_delete_property(
140                            source_id,
141                            key,
142                            &self.memory,
143                        )?
144                    }
145                }
146            }
147        };
148
149        Ok(())
150    }
151
152    async fn resolve_pointer_address_external(
153        &self,
154        address: PointerAddress,
155    ) -> Result<DIFReference, DIFResolveReferenceError> {
156        let reference = self.resolve_in_memory_reference(&address);
157        match reference {
158            Some(ptr) => Ok(DIFReference::from_reference(&ptr, &self.memory)),
159            None => todo!("#399 Implement async resolution of references"),
160        }
161    }
162
163    fn resolve_pointer_address_in_memory(
164        &self,
165        address: PointerAddress,
166    ) -> Result<DIFReference, DIFResolveReferenceError> {
167        let reference = self.resolve_in_memory_reference(&address);
168        match reference {
169            Some(ptr) => Ok(DIFReference::from_reference(&ptr, &self.memory)),
170            None => Err(DIFResolveReferenceError::ReferenceNotFound),
171        }
172    }
173
174    fn apply(
175        &self,
176        callee: DIFValueContainer,
177        value: DIFValueContainer,
178    ) -> Result<DIFValueContainer, DIFApplyError> {
179        todo!("#400 Undescribed by author.")
180    }
181
182    fn create_pointer(
183        &self,
184        value: DIFValueContainer,
185        allowed_type: Option<DIFTypeContainer>,
186        mutability: ReferenceMutability,
187    ) -> Result<PointerAddress, DIFCreatePointerError> {
188        let container = value.to_value_container(&self.memory)?;
189        let type_container = if let Some(allowed_type) = &allowed_type {
190            todo!(
191                "FIXME: Implement type_container creation from DIFTypeContainer"
192            )
193        } else {
194            None
195        };
196        let reference = Reference::try_new_from_value_container(
197            container,
198            type_container,
199            None,
200            mutability,
201        )?;
202        let address = self.memory.borrow_mut().register_reference(&reference);
203        Ok(address)
204    }
205
206    fn observe_pointer<F: Fn(&DIFUpdate) + 'static>(
207        &self,
208        transceiver_id: TransceiverId,
209        address: PointerAddress,
210        options: ObserveOptions,
211        callback: F,
212    ) -> Result<u32, DIFObserveError> {
213        let reference = self
214            .resolve_in_memory_reference(&address)
215            .ok_or(DIFObserveError::ReferenceNotFound)?;
216        Ok(reference.observe(Observer {
217            transceiver_id,
218            options,
219            callback: Rc::new(callback),
220        })?)
221    }
222
223    fn update_observer_options(
224        &self,
225        address: PointerAddress,
226        observer_id: u32,
227        options: ObserveOptions,
228    ) -> Result<(), DIFObserveError> {
229        let reference = self
230            .resolve_in_memory_reference(&address)
231            .ok_or(DIFObserveError::ReferenceNotFound)?;
232        reference
233            .update_observer_options(observer_id, options)
234            .map_err(DIFObserveError::ObserveError)
235    }
236
237    fn unobserve_pointer(
238        &self,
239        address: PointerAddress,
240        observer_id: u32,
241    ) -> Result<(), DIFObserveError> {
242        let reference = self
243            .resolve_in_memory_reference(&address)
244            .ok_or(DIFObserveError::ReferenceNotFound)?;
245        reference
246            .unobserve(observer_id)
247            .map_err(DIFObserveError::ObserveError)
248    }
249}
250
251#[cfg(test)]
252mod tests {
253    use crate::dif::interface::DIFInterface;
254    use crate::dif::representation::DIFValueRepresentation;
255    use crate::dif::update::{DIFUpdate, DIFUpdateData};
256    use crate::dif::value::{DIFValue, DIFValueContainer};
257    use crate::references::observers::ObserveOptions;
258    use crate::references::reference::ReferenceMutability;
259    use crate::runtime::Runtime;
260    use crate::runtime::memory::Memory;
261    use crate::values::core_values::endpoint::Endpoint;
262    use crate::values::core_values::map::Map;
263    use crate::values::value_container::ValueContainer;
264    use datex_core::runtime::RuntimeConfig;
265    use std::cell::RefCell;
266    use std::rc::Rc;
267
268    #[test]
269    fn struct_serde() {
270        let memory = RefCell::new(Memory::new(Endpoint::default()));
271        let map = ValueContainer::from(Map::from(vec![
272            ("a".to_string(), 1.into()),
273            ("b".to_string(), "text".into()),
274        ]));
275        let dif_value = DIFValueContainer::from_value_container(&map, &memory);
276        let _ = serde_json::to_string(&dif_value).unwrap();
277    }
278
279    #[test]
280    fn test_create_and_observe_pointer() {
281        let runtime = Runtime::init_native(RuntimeConfig::default()).internal;
282        let pointer_address = runtime
283            .create_pointer(
284                DIFValueContainer::Value(DIFValue::from(
285                    DIFValueRepresentation::String("Hello, world!".to_string()),
286                )),
287                None,
288                ReferenceMutability::Mutable,
289            )
290            .expect("Failed to create pointer");
291
292        let observed = Rc::new(RefCell::new(None));
293        let observed_clone = observed.clone();
294
295        let observer_id = Rc::new(RefCell::new(None));
296        let observer_id_clone = observer_id.clone();
297        let runtime_clone = runtime.clone();
298        let pointer_address_clone = pointer_address.clone();
299
300        // Observe the pointer
301        observer_id.replace(Some(
302            runtime
303                .observe_pointer(
304                    0,
305                    pointer_address_clone.clone(),
306                    ObserveOptions::default(),
307                    move |update| {
308                        println!("Observed pointer value: {:?}", update);
309                        observed_clone.replace(Some(update.clone()));
310                        // unobserve after first update
311                        runtime_clone
312                            .unobserve_pointer(
313                                pointer_address_clone.clone(),
314                                observer_id_clone.borrow().unwrap(),
315                            )
316                            .unwrap();
317                    },
318                )
319                .expect("Failed to observe pointer"),
320        ));
321
322        // Update the pointer value
323        runtime
324            .update(
325                1,
326                pointer_address.clone(),
327                DIFUpdateData::replace(DIFValue::from(
328                    DIFValueRepresentation::String("Hello, Datex!".to_string()),
329                )),
330            )
331            .expect("Failed to update pointer");
332
333        // Check if the observed value matches the update
334        let observed_value = observed.borrow();
335        assert_eq!(
336            *observed_value,
337            Some(DIFUpdate {
338                source_id: 1,
339                data: DIFUpdateData::replace(DIFValue::from(
340                    DIFValueRepresentation::String("Hello, Datex!".to_string(),)
341                ))
342            })
343        );
344
345        // try unobserve again, should fail
346        assert!(
347            runtime
348                .unobserve_pointer(
349                    pointer_address.clone(),
350                    observer_id.borrow().unwrap()
351                )
352                .is_err()
353        );
354    }
355}