datex_core/dif/
update.rs

1use crate::dif::{DIFConvertible, value::DIFValueContainer};
2use crate::references::observers::TransceiverId;
3use crate::runtime::memory::Memory;
4use crate::stdlib::borrow::Cow;
5use crate::stdlib::string::String;
6use crate::stdlib::string::ToString;
7use crate::stdlib::vec::Vec;
8use crate::values::value_container::ValueKey;
9use core::cell::RefCell;
10use core::prelude::rust_2024::*;
11use serde::{Deserialize, Serialize};
12
13/// Represents a key in the Datex Interface Format (DIF).
14#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
15#[serde(tag = "kind", rename_all = "lowercase", content = "value")]
16pub enum DIFKey {
17    /// a simple string property
18    Text(String),
19    /// an integer property (e.g. an array index)
20    // FIXME #385 use usize or u32 u64
21    Index(i64),
22    /// any other property type
23    Value(DIFValueContainer),
24}
25
26impl DIFKey {
27    pub fn from_value_key(key: &ValueKey, memory: &RefCell<Memory>) -> Self {
28        match key {
29            ValueKey::Text(s) => DIFKey::Text(s.to_string()),
30            ValueKey::Index(i) => DIFKey::Index(*i),
31            ValueKey::Value(v) => DIFKey::Value(
32                DIFValueContainer::from_value_container(v, memory),
33            ),
34        }
35    }
36}
37
38impl From<String> for DIFKey {
39    fn from(s: String) -> Self {
40        DIFKey::Text(s)
41    }
42}
43impl From<&str> for DIFKey {
44    fn from(s: &str) -> Self {
45        DIFKey::Text(s.to_string())
46    }
47}
48impl From<i64> for DIFKey {
49    fn from(i: i64) -> Self {
50        DIFKey::Index(i)
51    }
52}
53impl From<DIFValueContainer> for DIFKey {
54    fn from(v: DIFValueContainer) -> Self {
55        DIFKey::Value(v)
56    }
57}
58
59#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
60pub struct DIFUpdate<'a> {
61    pub source_id: TransceiverId,
62    pub data: Cow<'a, DIFUpdateData>,
63}
64
65impl<'a> DIFUpdate<'a> {
66    /// Creates a new `DIFUpdate` with the given source ID and update data.
67    pub fn new(source_id: TransceiverId, data: Cow<'a, DIFUpdateData>) -> Self {
68        DIFUpdate { source_id, data }
69    }
70}
71
72// TODO #386 optimize structural representation by using integer values for enum variants
73// and shrink down keys (kind: 0, 1, 2 instead of "clear", "set", "remove", ...)
74
75/// Represents an update operation for a DIF value.
76#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
77#[serde(tag = "kind", rename_all = "snake_case")]
78pub enum DIFUpdateData {
79    /// Represents a replacement operation for a DIF value.
80    Replace { value: DIFValueContainer },
81
82    /// Represents an update to a specific property of a DIF value.
83    /// The `key` specifies which property to update, and `value` is the new value for that property.
84    Set {
85        key: DIFKey,
86        value: DIFValueContainer,
87    },
88
89    /// Represents the removal of a specific property from a DIF value.
90    Delete { key: DIFKey },
91
92    /// Represents clearing all elements from a collection-type DIF value (like an array or map).
93    Clear,
94
95    /// Represents adding a new element to a collection-type DIF value (like an array or map).
96    Append { value: DIFValueContainer },
97
98    /// Special update operation for list values that allows splicing
99    ListSplice {
100        start: u32,
101        delete_count: u32,
102        items: Vec<DIFValueContainer>,
103    },
104}
105
106impl DIFConvertible for DIFUpdateData {}
107
108impl DIFUpdateData {
109    /// Creates a new `DIFUpdateData::Replace` variant with the given value.
110    pub fn replace(value: impl Into<DIFValueContainer>) -> Self {
111        DIFUpdateData::Replace {
112            value: value.into(),
113        }
114    }
115
116    /// Creates a new `DIFUpdateData::Set` variant with the given key and value.
117    pub fn set(
118        key: impl Into<DIFKey>,
119        value: impl Into<DIFValueContainer>,
120    ) -> Self {
121        DIFUpdateData::Set {
122            key: key.into(),
123            value: value.into(),
124        }
125    }
126
127    /// Creates a new `DIFUpdateData::Delete` variant with the given key.
128    pub fn delete(key: impl Into<DIFKey>) -> Self {
129        DIFUpdateData::Delete { key: key.into() }
130    }
131
132    /// Creates a new `DIFUpdateData::Clear` variant.
133    pub fn clear() -> Self {
134        DIFUpdateData::Clear
135    }
136
137    /// Creates a new `DIFUpdateData::Append` variant with the given value.
138    pub fn append(value: impl Into<DIFValueContainer>) -> Self {
139        DIFUpdateData::Append {
140            value: value.into(),
141        }
142    }
143
144    /// Creates a new `DIFUpdateData::ListSplice` variant with the given parameters.
145    pub fn list_splice(
146        range: core::ops::Range<u32>,
147        items: Vec<DIFValueContainer>,
148    ) -> Self {
149        DIFUpdateData::ListSplice {
150            start: range.start,
151            delete_count: range.end - range.start,
152            items,
153        }
154    }
155
156    pub fn with_source(&self, source_id: TransceiverId) -> DIFUpdate<'_> {
157        DIFUpdate {
158            source_id,
159            data: Cow::Borrowed(self),
160        }
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167    use crate::dif::representation::DIFValueRepresentation;
168    use crate::dif::value::DIFValue;
169
170    #[test]
171    fn serialize_replace() {
172        let dif_update =
173            DIFUpdateData::replace(DIFValueContainer::Value(DIFValue {
174                value: DIFValueRepresentation::String("Hello".to_string()),
175                ty: None,
176            }));
177        let serialized = dif_update.as_json();
178        assert_eq!(
179            serialized,
180            r#"{"kind":"replace","value":{"value":"Hello"}}"#
181        );
182        let deserialized = DIFUpdateData::from_json(&serialized);
183        assert_eq!(dif_update, deserialized);
184    }
185
186    #[test]
187    fn serialize_set() {
188        let dif_update = DIFUpdateData::set(
189            "name",
190            DIFValueContainer::Value(DIFValue {
191                value: DIFValueRepresentation::Number(42.0),
192                ty: None,
193            }),
194        );
195        let serialized = dif_update.as_json();
196        assert_eq!(
197            serialized,
198            r#"{"kind":"set","key":{"kind":"text","value":"name"},"value":{"value":42.0}}"#
199        );
200        let deserialized = DIFUpdateData::from_json(&serialized);
201        assert_eq!(dif_update, deserialized);
202    }
203
204    #[test]
205    fn serialize_remove() {
206        let dif_update = DIFUpdateData::delete("age");
207        let serialized = dif_update.as_json();
208        assert_eq!(
209            serialized,
210            r#"{"kind":"delete","key":{"kind":"text","value":"age"}}"#
211        );
212        let deserialized = DIFUpdateData::from_json(&serialized);
213        assert_eq!(dif_update, deserialized);
214    }
215
216    #[test]
217    fn serialize_clear() {
218        let dif_update = DIFUpdateData::clear();
219        let serialized = dif_update.as_json();
220        assert_eq!(serialized, r#"{"kind":"clear"}"#);
221        let deserialized = DIFUpdateData::from_json(&serialized);
222        assert_eq!(dif_update, deserialized);
223    }
224
225    #[test]
226    fn serialize_push() {
227        let dif_update =
228            DIFUpdateData::append(DIFValueContainer::Value(DIFValue {
229                value: DIFValueRepresentation::Boolean(true),
230                ty: None,
231            }));
232        let serialized = dif_update.as_json();
233        assert_eq!(serialized, r#"{"kind":"append","value":{"value":true}}"#);
234        let deserialized = DIFUpdateData::from_json(&serialized);
235        assert_eq!(dif_update, deserialized);
236    }
237}