datex_core/dif/
value.rs

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