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        value::Value,
17        value_container::ValueContainer,
18    },
19};
20use core::{cell::RefCell, result::Result};
21use serde::{Deserialize, Serialize};
22
23use crate::{prelude::*, shared_values::pointer_address::PointerAddress};
24
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::Shared(
91                memory
92                    .borrow()
93                    .get_reference(address)
94                    .ok_or(DIFReferenceNotFoundError)?
95                    .clone(),
96            ),
97            DIFValueContainer::Value(v) => {
98                ValueContainer::Local(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(value_container: &ValueContainer) -> Self {
122        match value_container {
123            ValueContainer::Shared(reference) => {
124                DIFValueContainer::Reference(reference.pointer_address())
125            }
126            ValueContainer::Local(value) => {
127                DIFValueContainer::Value(DIFValue::from_value(value))
128            }
129        }
130    }
131}
132
133impl DIFValue {
134    fn from_value(value: &Value) -> Self {
135        let core_value = &value.inner;
136
137        let mut is_empty_map = false;
138
139        let dif_core_value = match core_value {
140            CoreValue::Type(_ty) => {
141                core::todo!("#382 Type value not supported in DIF")
142            }
143            CoreValue::Callable(_callable) => {
144                core::todo!("#616 Callable value not yet supported in DIF")
145            }
146            CoreValue::Null => DIFValueRepresentation::Null,
147            CoreValue::Boolean(bool) => DIFValueRepresentation::Boolean(bool.0),
148            CoreValue::Integer(integer) => {
149                // TODO #383: optimize this and pass as integer if in range
150                DIFValueRepresentation::String(integer.to_string())
151            }
152            CoreValue::TypedInteger(integer) => {
153                match integer {
154                    TypedInteger::I8(i) => {
155                        DIFValueRepresentation::Number(*i as f64)
156                    }
157                    TypedInteger::U8(u) => {
158                        DIFValueRepresentation::Number(*u as f64)
159                    }
160                    TypedInteger::I16(i) => {
161                        DIFValueRepresentation::Number(*i as f64)
162                    }
163                    TypedInteger::U16(u) => {
164                        DIFValueRepresentation::Number(*u as f64)
165                    }
166                    TypedInteger::I32(i) => {
167                        DIFValueRepresentation::Number(*i as f64)
168                    }
169                    TypedInteger::U32(u) => {
170                        DIFValueRepresentation::Number(*u as f64)
171                    }
172                    // i64 and above are serialized as strings in DIF
173                    TypedInteger::I64(i) => {
174                        DIFValueRepresentation::String(i.to_string())
175                    }
176                    TypedInteger::U64(u) => {
177                        DIFValueRepresentation::String(u.to_string())
178                    }
179                    TypedInteger::I128(i) => {
180                        DIFValueRepresentation::String(i.to_string())
181                    }
182                    TypedInteger::U128(u) => {
183                        DIFValueRepresentation::String(u.to_string())
184                    }
185                    TypedInteger::IBig(i) => {
186                        DIFValueRepresentation::String(i.to_string())
187                    }
188                }
189            }
190            CoreValue::Range(_range) => {
191                core::todo!("#740 Range value not yet supported in DIF")
192            }
193            CoreValue::Decimal(decimal) => {
194                // TODO #384: optimize this and pass as decimal if in range
195                DIFValueRepresentation::String(decimal.to_string())
196            }
197            CoreValue::TypedDecimal(decimal) => match decimal {
198                TypedDecimal::F32(f) => {
199                    DIFValueRepresentation::Number(f.0 as f64)
200                }
201                TypedDecimal::F64(f) => DIFValueRepresentation::Number(f.0),
202                TypedDecimal::Decimal(bd) => {
203                    DIFValueRepresentation::String(bd.to_string())
204                }
205            },
206            CoreValue::Text(text) => {
207                DIFValueRepresentation::String(text.0.clone())
208            }
209            CoreValue::Endpoint(endpoint) => {
210                DIFValueRepresentation::String(endpoint.to_string())
211            }
212            CoreValue::List(list) => DIFValueRepresentation::Array(
213                list.iter()
214                    .map(DIFValueContainer::from_value_container)
215                    .collect(),
216            ),
217            CoreValue::Map(map) => match map {
218                Map::StructuralWithStringKeys(entries) => {
219                    DIFValueRepresentation::Object(
220                        entries
221                            .iter()
222                            .map(|(k, v)| {
223                                (
224                                    k.clone(),
225                                    DIFValueContainer::from_value_container(v),
226                                )
227                            })
228                            .collect(),
229                    )
230                }
231                _ => {
232                    if map.is_empty() {
233                        is_empty_map = true;
234                    };
235
236                    DIFValueRepresentation::Map(
237                        map.iter()
238                            .map(|(k, v)| {
239                                (
240                                    match k {
241                                        BorrowedMapKey::Text(text_key) => {
242                                            DIFValueContainer::Value(
243                                                DIFValueRepresentation::String(
244                                                    text_key.to_string(),
245                                                )
246                                                    .into(),
247                                            )
248                                        }
249                                        _ => {
250                                            DIFValueContainer::from_value_container(
251                                                &ValueContainer::from(k),
252                                            )
253                                        }
254                                    },
255                                    DIFValueContainer::from_value_container(
256                                        v
257                                    ),
258                                )
259                            })
260                            .collect(),
261                    )
262                }
263            },
264        };
265
266        DIFValue {
267            value: dif_core_value,
268            ty: get_type_if_non_default(&value.actual_type, is_empty_map),
269        }
270    }
271}
272
273/// Returns the type if it is not the default type for the value, None otherwise
274/// We treat the following types as default:
275/// - boolean
276/// - text
277/// - null
278/// - decimal (f64)
279/// - List
280/// - Map (if not empty, otherwise we cannot distinguish between empty map and empty list since both are represented as [] in DIF)
281fn get_type_if_non_default(
282    type_definition: &TypeDefinition,
283    is_empty_map: bool,
284) -> Option<DIFTypeDefinition> {
285    match type_definition {
286        TypeDefinition::SharedReference(inner) => {
287            if let Ok(address) =
288                CoreLibPointerId::try_from(&inner.borrow().pointer.address())
289                && (core::matches!(
290                        address,
291                        CoreLibPointerId::Decimal(Some(DecimalTypeVariant::F64))
292                            | CoreLibPointerId::Boolean
293                            | CoreLibPointerId::Text
294                            | CoreLibPointerId::List
295                            | CoreLibPointerId::Null
296                    ) ||
297                    // map is default only if not empty
298                    (core::matches!(address, CoreLibPointerId::Map) && !is_empty_map))
299            {
300                None
301            } else {
302                Some(DIFTypeDefinition::from_type_definition(type_definition))
303            }
304        }
305        _ => Some(DIFTypeDefinition::from_type_definition(type_definition)),
306    }
307}
308
309#[cfg(test)]
310mod tests {
311    use crate::{
312        dif::{DIFConvertible, r#type::DIFTypeDefinition, value::DIFValue},
313        libs::core::CoreLibPointerId,
314        runtime::memory::Memory,
315        values::{
316            core_values::{
317                endpoint::Endpoint, integer::typed_integer::IntegerTypeVariant,
318                map::Map,
319            },
320            value_container::ValueContainer,
321        },
322    };
323
324    use crate::{prelude::*, values::value::Value};
325    use core::cell::RefCell;
326
327    fn get_mock_memory() -> RefCell<Memory> {
328        RefCell::new(Memory::new(Endpoint::default()))
329    }
330
331    #[test]
332    fn default_type() {
333        let dif = DIFValue::from_value(&Value::from(true));
334        assert!(dif.ty.is_none());
335
336        let dif = DIFValue::from_value(&Value::from("hello"));
337        assert!(dif.ty.is_none());
338
339        let dif = DIFValue::from_value(&Value::null());
340        assert!(dif.ty.is_none());
341
342        let dif = DIFValue::from_value(&Value::from(3.5f64));
343        assert!(dif.ty.is_none());
344
345        let dif = DIFValue::from_value(&Value::from(vec![
346            Value::from(1),
347            Value::from(2),
348            Value::from(3),
349        ]));
350        assert!(dif.ty.is_none());
351
352        let dif = DIFValue::from_value(&Value::from(Map::from(vec![
353            ("a".to_string(), ValueContainer::from(1)),
354            ("b".to_string(), ValueContainer::from(2)),
355        ])));
356        assert!(dif.ty.is_none());
357    }
358
359    #[test]
360    fn non_default_type() {
361        let dif = DIFValue::from_value(&Value::from(123u16));
362        assert!(dif.ty.is_some());
363        if let DIFTypeDefinition::Reference(reference) = dif.ty.unwrap() {
364            assert_eq!(
365                reference,
366                CoreLibPointerId::Integer(Some(IntegerTypeVariant::U16)).into()
367            );
368        } else {
369            core::panic!("Expected reference type");
370        }
371
372        let dif = DIFValue::from_value(&Value::from(123i64));
373        assert!(dif.ty.is_some());
374        if let DIFTypeDefinition::Reference(reference) = dif.ty.unwrap() {
375            assert_eq!(
376                reference,
377                CoreLibPointerId::Integer(Some(IntegerTypeVariant::I64)).into()
378            );
379        } else {
380            core::panic!("Expected reference type");
381        }
382    }
383
384    #[test]
385    fn serde_dif_value() {
386        let dif = DIFValue::from_value(&Value::from("Hello, world!"));
387        let serialized = dif.as_json();
388        let deserialized = DIFValue::from_json(&serialized);
389        assert_eq!(dif, deserialized);
390    }
391}