Skip to main content

datex_core/dif/
update.rs

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