Skip to main content

datex_core/dif/
value.rs

1use crate::{
2    dif::{
3        DIFConvertible, representation::DIFValueRepresentation,
4        r#type::DIFTypeDefinition,
5    },
6    libs::core::CoreLibPointerId,
7    runtime::memory::Memory,
8    types::definition::TypeDefinition,
9    values::{
10        core_value::CoreValue,
11        core_values::{
12            decimal::typed_decimal::{DecimalTypeVariant, TypedDecimal},
13            integer::typed_integer::TypedInteger,
14            map::{BorrowedMapKey, Map},
15        },
16        pointer::PointerAddress,
17        value::Value,
18        value_container::ValueContainer,
19    },
20};
21use core::{cell::RefCell, result::Result};
22use serde::{Deserialize, Serialize};
23
24use crate::prelude::*;
25#[derive(Debug)]
26pub struct DIFReferenceNotFoundError;
27
28/// Represents a value in the Datex Interface Format (DIF).
29#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
30pub struct DIFValue {
31    pub value: DIFValueRepresentation,
32    #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
33    pub ty: Option<DIFTypeDefinition>,
34}
35impl DIFConvertible for DIFValue {}
36
37impl DIFValue {
38    /// Converts the DIFValue into a Value, resolving references using the provided memory.
39    /// Returns an error if a reference cannot be resolved.
40    pub fn to_value(
41        &self,
42        memory: &RefCell<Memory>,
43    ) -> Result<Value, DIFReferenceNotFoundError> {
44        Ok(if let Some(ty) = &self.ty {
45            self.value.to_value_with_type(ty, memory)?
46        } else {
47            self.value.to_default_value(memory)?
48        })
49    }
50}
51
52impl DIFValue {
53    pub fn new(
54        value: DIFValueRepresentation,
55        ty: Option<impl Into<DIFTypeDefinition>>,
56    ) -> Self {
57        DIFValue {
58            value,
59            ty: ty.map(Into::into),
60        }
61    }
62    pub fn as_container(&self) -> DIFValueContainer {
63        DIFValueContainer::from(self.clone())
64    }
65}
66
67impl From<DIFValueRepresentation> for DIFValue {
68    fn from(value: DIFValueRepresentation) -> Self {
69        DIFValue { value, ty: None }
70    }
71}
72
73/// Holder for either a value or a reference to a value in DIF
74#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
75#[serde(untagged)]
76pub enum DIFValueContainer {
77    Value(DIFValue),
78    Reference(PointerAddress),
79}
80impl DIFConvertible for DIFValueContainer {}
81
82impl DIFValueContainer {
83    /// Converts the DIFValueContainer into a ValueContainer, resolving references using the provided memory.
84    /// Returns an error if a reference cannot be resolved.
85    pub fn to_value_container(
86        &self,
87        memory: &RefCell<Memory>,
88    ) -> Result<ValueContainer, DIFReferenceNotFoundError> {
89        Ok(match self {
90            DIFValueContainer::Reference(address) => ValueContainer::Reference(
91                memory
92                    .borrow()
93                    .get_reference(address)
94                    .ok_or(DIFReferenceNotFoundError)?
95                    .clone(),
96            ),
97            DIFValueContainer::Value(v) => {
98                ValueContainer::Value(v.to_value(memory)?)
99            }
100        })
101    }
102}
103
104impl From<DIFValue> for DIFValueContainer {
105    fn from(value: DIFValue) -> Self {
106        DIFValueContainer::Value(value)
107    }
108}
109impl From<&DIFValue> for DIFValueContainer {
110    fn from(value: &DIFValue) -> Self {
111        DIFValueContainer::Value(value.clone())
112    }
113}
114impl From<PointerAddress> for DIFValueContainer {
115    fn from(ptr: PointerAddress) -> Self {
116        DIFValueContainer::Reference(ptr)
117    }
118}
119
120impl DIFValueContainer {
121    pub fn from_value_container(
122        value_container: &ValueContainer,
123        memory: &RefCell<Memory>,
124    ) -> Self {
125        match value_container {
126            ValueContainer::Reference(reference) => {
127                // get pointer address, if not present register the reference in memory
128                let address = reference.ensure_pointer_address(memory);
129                DIFValueContainer::Reference(address)
130            }
131            ValueContainer::Value(value) => {
132                DIFValueContainer::Value(DIFValue::from_value(value, memory))
133            }
134        }
135    }
136}
137
138impl DIFValue {
139    fn from_value(value: &Value, memory: &RefCell<Memory>) -> Self {
140        let core_value = &value.inner;
141
142        let mut is_empty_map = false;
143
144        let dif_core_value = match core_value {
145            CoreValue::Type(_ty) => {
146                core::todo!("#382 Type value not supported in DIF")
147            }
148            CoreValue::Callable(_callable) => {
149                core::todo!("#616 Callable value not yet supported in DIF")
150            }
151            CoreValue::Null => DIFValueRepresentation::Null,
152            CoreValue::Boolean(bool) => DIFValueRepresentation::Boolean(bool.0),
153            CoreValue::Integer(integer) => {
154                // TODO #383: optimize this and pass as integer if in range
155                DIFValueRepresentation::String(integer.to_string())
156            }
157            CoreValue::TypedInteger(integer) => {
158                match integer {
159                    TypedInteger::I8(i) => {
160                        DIFValueRepresentation::Number(*i as f64)
161                    }
162                    TypedInteger::U8(u) => {
163                        DIFValueRepresentation::Number(*u as f64)
164                    }
165                    TypedInteger::I16(i) => {
166                        DIFValueRepresentation::Number(*i as f64)
167                    }
168                    TypedInteger::U16(u) => {
169                        DIFValueRepresentation::Number(*u as f64)
170                    }
171                    TypedInteger::I32(i) => {
172                        DIFValueRepresentation::Number(*i as f64)
173                    }
174                    TypedInteger::U32(u) => {
175                        DIFValueRepresentation::Number(*u as f64)
176                    }
177                    // i64 and above are serialized as strings in DIF
178                    TypedInteger::I64(i) => {
179                        DIFValueRepresentation::String(i.to_string())
180                    }
181                    TypedInteger::U64(u) => {
182                        DIFValueRepresentation::String(u.to_string())
183                    }
184                    TypedInteger::I128(i) => {
185                        DIFValueRepresentation::String(i.to_string())
186                    }
187                    TypedInteger::U128(u) => {
188                        DIFValueRepresentation::String(u.to_string())
189                    }
190                    TypedInteger::IBig(i) => {
191                        DIFValueRepresentation::String(i.to_string())
192                    }
193                }
194            }
195            CoreValue::Range(_range) => {
196                core::todo!("#740 Range value not yet supported in DIF")
197            }
198            CoreValue::Decimal(decimal) => {
199                // TODO #384: optimize this and pass as decimal if in range
200                DIFValueRepresentation::String(decimal.to_string())
201            }
202            CoreValue::TypedDecimal(decimal) => match decimal {
203                TypedDecimal::F32(f) => {
204                    DIFValueRepresentation::Number(f.0 as f64)
205                }
206                TypedDecimal::F64(f) => DIFValueRepresentation::Number(f.0),
207                TypedDecimal::Decimal(bd) => {
208                    DIFValueRepresentation::String(bd.to_string())
209                }
210            },
211            CoreValue::Text(text) => {
212                DIFValueRepresentation::String(text.0.clone())
213            }
214            CoreValue::Endpoint(endpoint) => {
215                DIFValueRepresentation::String(endpoint.to_string())
216            }
217            CoreValue::List(list) => DIFValueRepresentation::Array(
218                list.iter()
219                    .map(|v| DIFValueContainer::from_value_container(v, memory))
220                    .collect(),
221            ),
222            CoreValue::Map(map) => match map {
223                Map::StructuralWithStringKeys(entries) => {
224                    DIFValueRepresentation::Object(
225                        entries
226                            .iter()
227                            .map(|(k, v)| {
228                                (
229                                    k.clone(),
230                                    DIFValueContainer::from_value_container(
231                                        v, memory,
232                                    ),
233                                )
234                            })
235                            .collect(),
236                    )
237                }
238                _ => {
239                    if map.is_empty() {
240                        is_empty_map = true;
241                    };
242
243                    DIFValueRepresentation::Map(
244                        map.iter()
245                            .map(|(k, v)| {
246                                (
247                                    match k {
248                                        BorrowedMapKey::Text(text_key) => {
249                                            DIFValueContainer::Value(
250                                                DIFValueRepresentation::String(
251                                                    text_key.to_string(),
252                                                )
253                                                    .into(),
254                                            )
255                                        }
256                                        _ => {
257                                            DIFValueContainer::from_value_container(
258                                                &ValueContainer::from(k),
259                                                memory,
260                                            )
261                                        }
262                                    },
263                                    DIFValueContainer::from_value_container(
264                                        v, memory,
265                                    ),
266                                )
267                            })
268                            .collect(),
269                    )
270                }
271            },
272        };
273
274        DIFValue {
275            value: dif_core_value,
276            ty: get_type_if_non_default(
277                &value.actual_type,
278                memory,
279                is_empty_map,
280            ),
281        }
282    }
283}
284
285/// Returns the type if it is not the default type for the value, None otherwise
286/// We treat the following types as default:
287/// - boolean
288/// - text
289/// - null
290/// - decimal (f64)
291/// - List
292/// - Map (if not empty, otherwise we cannot distinguish between empty map and empty list since both are represented as [] in DIF)
293fn get_type_if_non_default(
294    type_definition: &TypeDefinition,
295    memory: &RefCell<Memory>,
296    is_empty_map: bool,
297) -> Option<DIFTypeDefinition> {
298    match type_definition {
299        TypeDefinition::Reference(inner) => {
300            if let Some(Ok(address)) = inner
301                .borrow()
302                .pointer_address
303                .as_ref()
304                .map(CoreLibPointerId::try_from)
305                && (core::matches!(
306                        address,
307                        CoreLibPointerId::Decimal(Some(DecimalTypeVariant::F64))
308                            | CoreLibPointerId::Boolean
309                            | CoreLibPointerId::Text
310                            | CoreLibPointerId::List
311                            | CoreLibPointerId::Null
312                    ) ||
313                    // map is default only if not empty
314                    (core::matches!(address, CoreLibPointerId::Map) && !is_empty_map))
315            {
316                None
317            } else {
318                Some(DIFTypeDefinition::from_type_definition(
319                    type_definition,
320                    memory,
321                ))
322            }
323        }
324        _ => Some(DIFTypeDefinition::from_type_definition(
325            type_definition,
326            memory,
327        )),
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    use crate::{
334        dif::{DIFConvertible, r#type::DIFTypeDefinition, value::DIFValue},
335        libs::core::CoreLibPointerId,
336        runtime::memory::Memory,
337        values::{
338            core_values::{
339                endpoint::Endpoint, integer::typed_integer::IntegerTypeVariant,
340                map::Map,
341            },
342            value_container::ValueContainer,
343        },
344    };
345
346    use crate::{prelude::*, values::value::Value};
347    use core::cell::RefCell;
348
349    fn get_mock_memory() -> RefCell<Memory> {
350        RefCell::new(Memory::new(Endpoint::default()))
351    }
352
353    #[test]
354    fn default_type() {
355        let memory = get_mock_memory();
356        let dif = DIFValue::from_value(&Value::from(true), &memory);
357        assert!(dif.ty.is_none());
358
359        let dif = DIFValue::from_value(&Value::from("hello"), &memory);
360        assert!(dif.ty.is_none());
361
362        let dif = DIFValue::from_value(&Value::null(), &memory);
363        assert!(dif.ty.is_none());
364
365        let dif = DIFValue::from_value(&Value::from(3.5f64), &memory);
366        assert!(dif.ty.is_none());
367
368        let dif = DIFValue::from_value(
369            &Value::from(vec![Value::from(1), Value::from(2), Value::from(3)]),
370            &memory,
371        );
372        assert!(dif.ty.is_none());
373
374        let dif = DIFValue::from_value(
375            &Value::from(Map::from(vec![
376                ("a".to_string(), ValueContainer::from(1)),
377                ("b".to_string(), ValueContainer::from(2)),
378            ])),
379            &memory,
380        );
381        assert!(dif.ty.is_none());
382    }
383
384    #[test]
385    fn non_default_type() {
386        let memory = get_mock_memory();
387        let dif = DIFValue::from_value(&Value::from(123u16), &memory);
388        assert!(dif.ty.is_some());
389        if let DIFTypeDefinition::Reference(reference) = dif.ty.unwrap() {
390            assert_eq!(
391                reference,
392                CoreLibPointerId::Integer(Some(IntegerTypeVariant::U16)).into()
393            );
394        } else {
395            core::panic!("Expected reference type");
396        }
397
398        let dif = DIFValue::from_value(&Value::from(123i64), &memory);
399        assert!(dif.ty.is_some());
400        if let DIFTypeDefinition::Reference(reference) = dif.ty.unwrap() {
401            assert_eq!(
402                reference,
403                CoreLibPointerId::Integer(Some(IntegerTypeVariant::I64)).into()
404            );
405        } else {
406            core::panic!("Expected reference type");
407        }
408    }
409
410    #[test]
411    fn serde_dif_value() {
412        let memory = get_mock_memory();
413        let dif = DIFValue::from_value(&Value::from("Hello, world!"), &memory);
414        let serialized = dif.as_json();
415        let deserialized = DIFValue::from_json(&serialized);
416        assert_eq!(dif, deserialized);
417    }
418}