datex_core/dif/
value.rs

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