datex_core/dif/
update.rs

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