Skip to main content

datex_core/dif/
update.rs

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