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) => DIFValueRepresentation::Array(vec![
191                DIFValueContainer::from_value_container(&range.start),
192                DIFValueContainer::from_value_container(&range.end),
193            ]),
194            CoreValue::Decimal(decimal) => {
195                // TODO #384: optimize this and pass as decimal if in range
196                DIFValueRepresentation::String(decimal.to_string())
197            }
198            CoreValue::TypedDecimal(decimal) => match decimal {
199                TypedDecimal::F32(f) => {
200                    DIFValueRepresentation::Number(f.0 as f64)
201                }
202                TypedDecimal::F64(f) => DIFValueRepresentation::Number(f.0),
203                TypedDecimal::Decimal(bd) => {
204                    DIFValueRepresentation::String(bd.to_string())
205                }
206            },
207            CoreValue::Text(text) => {
208                DIFValueRepresentation::String(text.0.clone())
209            }
210            CoreValue::Endpoint(endpoint) => {
211                DIFValueRepresentation::String(endpoint.to_string())
212            }
213            CoreValue::List(list) => DIFValueRepresentation::Array(
214                list.iter()
215                    .map(DIFValueContainer::from_value_container)
216                    .collect(),
217            ),
218            CoreValue::Map(map) => match map {
219                Map::StructuralWithStringKeys(entries) => {
220                    DIFValueRepresentation::Object(
221                        entries
222                            .iter()
223                            .map(|(k, v)| {
224                                (
225                                    k.clone(),
226                                    DIFValueContainer::from_value_container(v),
227                                )
228                            })
229                            .collect(),
230                    )
231                }
232                _ => {
233                    if map.is_empty() {
234                        is_empty_map = true;
235                    };
236
237                    DIFValueRepresentation::Map(
238                        map.iter()
239                            .map(|(k, v)| {
240                                (
241                                    match k {
242                                        BorrowedMapKey::Text(text_key) => {
243                                            DIFValueContainer::Value(
244                                                DIFValueRepresentation::String(
245                                                    text_key.to_string(),
246                                                )
247                                                    .into(),
248                                            )
249                                        }
250                                        _ => {
251                                            DIFValueContainer::from_value_container(
252                                                &ValueContainer::from(k),
253                                            )
254                                        }
255                                    },
256                                    DIFValueContainer::from_value_container(
257                                        v
258                                    ),
259                                )
260                            })
261                            .collect(),
262                    )
263                }
264            },
265        };
266
267        DIFValue {
268            value: dif_core_value,
269            ty: get_type_if_non_default(&value.actual_type, is_empty_map),
270        }
271    }
272}
273
274/// Returns the type if it is not the default type for the value, None otherwise
275/// We treat the following types as default:
276/// - boolean
277/// - text
278/// - null
279/// - decimal (f64)
280/// - List
281/// - Map (if not empty, otherwise we cannot distinguish between empty map and empty list since both are represented as [] in DIF)
282fn get_type_if_non_default(
283    type_definition: &TypeDefinition,
284    is_empty_map: bool,
285) -> Option<DIFTypeDefinition> {
286    match type_definition {
287        TypeDefinition::SharedReference(inner) => {
288            if let Ok(address) =
289                CoreLibPointerId::try_from(&inner.borrow().pointer.address())
290                && (core::matches!(
291                        address,
292                        CoreLibPointerId::Decimal(Some(DecimalTypeVariant::F64))
293                            | CoreLibPointerId::Boolean
294                            | CoreLibPointerId::Text
295                            | CoreLibPointerId::List
296                            | CoreLibPointerId::Null
297                    ) ||
298                    // map is default only if not empty
299                    (core::matches!(address, CoreLibPointerId::Map) && !is_empty_map))
300            {
301                None
302            } else {
303                Some(DIFTypeDefinition::from_type_definition(type_definition))
304            }
305        }
306        _ => Some(DIFTypeDefinition::from_type_definition(type_definition)),
307    }
308}
309
310#[cfg(test)]
311mod tests {
312    use crate::{
313        dif::{DIFConvertible, r#type::DIFTypeDefinition, value::DIFValue},
314        libs::core::CoreLibPointerId,
315        runtime::memory::Memory,
316        values::{
317            core_values::{
318                endpoint::Endpoint, integer::typed_integer::IntegerTypeVariant,
319                map::Map,
320            },
321            value_container::ValueContainer,
322        },
323    };
324
325    use crate::{prelude::*, values::value::Value};
326    use core::cell::RefCell;
327
328    #[test]
329    fn default_type() {
330        let dif = DIFValue::from_value(&Value::from(true));
331        assert!(dif.ty.is_none());
332
333        let dif = DIFValue::from_value(&Value::from("hello"));
334        assert!(dif.ty.is_none());
335
336        let dif = DIFValue::from_value(&Value::null());
337        assert!(dif.ty.is_none());
338
339        let dif = DIFValue::from_value(&Value::from(3.5f64));
340        assert!(dif.ty.is_none());
341
342        let dif = DIFValue::from_value(&Value::from(vec![
343            Value::from(1),
344            Value::from(2),
345            Value::from(3),
346        ]));
347        assert!(dif.ty.is_none());
348
349        let dif = DIFValue::from_value(&Value::from(Map::from(vec![
350            ("a".to_string(), ValueContainer::from(1)),
351            ("b".to_string(), ValueContainer::from(2)),
352        ])));
353        assert!(dif.ty.is_none());
354    }
355
356    #[test]
357    fn non_default_type() {
358        let dif = DIFValue::from_value(&Value::from(123u16));
359        assert!(dif.ty.is_some());
360        if let DIFTypeDefinition::Reference(reference) = dif.ty.unwrap() {
361            assert_eq!(
362                reference,
363                CoreLibPointerId::Integer(Some(IntegerTypeVariant::U16)).into()
364            );
365        } else {
366            core::panic!("Expected reference type");
367        }
368
369        let dif = DIFValue::from_value(&Value::from(123i64));
370        assert!(dif.ty.is_some());
371        if let DIFTypeDefinition::Reference(reference) = dif.ty.unwrap() {
372            assert_eq!(
373                reference,
374                CoreLibPointerId::Integer(Some(IntegerTypeVariant::I64)).into()
375            );
376        } else {
377            core::panic!("Expected reference type");
378        }
379    }
380
381    #[test]
382    fn serde_dif_value() {
383        let dif = DIFValue::from_value(&Value::from("Hello, world!"));
384        let serialized = dif.as_json();
385        let deserialized = DIFValue::from_json(&serialized);
386        assert_eq!(dif, deserialized);
387    }
388}