waves_rust/model/transaction/
data_transaction.rs

1use crate::error::{Error, Result};
2use crate::model::data_entry::DataEntry;
3use crate::util::{Base64, JsonDeserializer};
4use crate::waves_proto::data_transaction_data::data_entry::Value::{
5    BinaryValue, BoolValue, IntValue, StringValue,
6};
7use crate::waves_proto::data_transaction_data::DataEntry as ProtoDataEntry;
8use crate::waves_proto::DataTransactionData;
9use serde_json::{Map, Number, Value};
10
11const TYPE: u8 = 12;
12
13#[derive(Clone, Eq, PartialEq, Debug)]
14pub struct DataTransactionInfo {
15    data: Vec<DataEntry>,
16}
17
18impl DataTransactionInfo {
19    pub fn new(data: Vec<DataEntry>) -> Self {
20        DataTransactionInfo { data }
21    }
22
23    pub fn tx_type() -> u8 {
24        TYPE
25    }
26
27    pub fn data(&self) -> Vec<DataEntry> {
28        self.data.clone()
29    }
30}
31
32impl TryFrom<&Value> for DataTransactionInfo {
33    type Error = Error;
34
35    fn try_from(value: &Value) -> Result<Self> {
36        let data_transaction: DataTransaction = value.try_into()?;
37        Ok(DataTransactionInfo {
38            data: data_transaction.data(),
39        })
40    }
41}
42
43#[derive(Clone, Eq, PartialEq, Debug)]
44pub struct DataTransaction {
45    data: Vec<DataEntry>,
46}
47
48impl DataTransaction {
49    pub fn new(data: Vec<DataEntry>) -> Self {
50        DataTransaction { data }
51    }
52
53    pub fn tx_type() -> u8 {
54        TYPE
55    }
56
57    pub fn data(&self) -> Vec<DataEntry> {
58        self.data.clone()
59    }
60}
61
62impl TryFrom<&Value> for DataTransaction {
63    type Error = Error;
64
65    fn try_from(value: &Value) -> Result<Self> {
66        let data_array = JsonDeserializer::safe_to_array_from_field(value, "data")?;
67        let data = data_array
68            .iter()
69            .map(|entry| entry.try_into())
70            .collect::<Result<Vec<DataEntry>>>()?;
71
72        Ok(DataTransaction { data })
73    }
74}
75
76impl TryFrom<&DataTransaction> for DataTransactionData {
77    type Error = Error;
78
79    fn try_from(value: &DataTransaction) -> Result<Self> {
80        let mut proto_data_entries: Vec<ProtoDataEntry> = vec![];
81        let data_entries = value.data();
82        for data_entry in data_entries {
83            let key = data_entry.key();
84            match data_entry {
85                DataEntry::IntegerEntry { key: _, value } => {
86                    proto_data_entries.push(ProtoDataEntry {
87                        key,
88                        value: Some(IntValue(value)),
89                    });
90                }
91                DataEntry::BooleanEntry { key: _, value } => {
92                    proto_data_entries.push(ProtoDataEntry {
93                        key,
94                        value: Some(BoolValue(value)),
95                    });
96                }
97                DataEntry::BinaryEntry { key: _, value } => {
98                    proto_data_entries.push(ProtoDataEntry {
99                        key,
100                        value: Some(BinaryValue(value)),
101                    })
102                }
103                DataEntry::StringEntry { key: _, value } => {
104                    proto_data_entries.push(ProtoDataEntry {
105                        key,
106                        value: Some(StringValue(value)),
107                    })
108                }
109                DataEntry::DeleteEntry { key: _ } => {
110                    proto_data_entries.push(ProtoDataEntry { key, value: None });
111                }
112            };
113        }
114        Ok(DataTransactionData {
115            data: proto_data_entries,
116        })
117    }
118}
119
120impl TryFrom<&DataTransaction> for Map<String, Value> {
121    type Error = Error;
122
123    fn try_from(value: &DataTransaction) -> Result<Self> {
124        let mut map: Map<String, Value> = Map::new();
125        let entries = value
126            .data()
127            .iter()
128            .map(|entry| entry.into())
129            .collect::<Vec<Value>>();
130        map.insert("data".to_string(), Value::Array(entries));
131        Ok(map)
132    }
133}
134
135impl From<&DataEntry> for Value {
136    fn from(data_entry: &DataEntry) -> Self {
137        let mut map: Map<String, Value> = Map::new();
138        map.insert("key".to_string(), data_entry.key().into());
139        match data_entry {
140            DataEntry::IntegerEntry { key: _, value } => {
141                map.insert("type".to_string(), "integer".into());
142                map.insert("value".to_string(), Value::Number(Number::from(*value)));
143            }
144            DataEntry::BooleanEntry { key: _, value } => {
145                map.insert("type".to_string(), "boolean".into());
146                map.insert("value".to_string(), Value::Bool(*value));
147            }
148            DataEntry::BinaryEntry { key: _, value } => {
149                map.insert("type".to_string(), "binary".into());
150                map.insert("value".to_string(), Base64::encode(value, true).into());
151            }
152            DataEntry::StringEntry { key: _, value } => {
153                map.insert("type".to_string(), "string".into());
154                map.insert("value".to_string(), Value::String(value.clone()));
155            }
156            DataEntry::DeleteEntry { key: _ } => {
157                map.insert("value".to_string(), Value::Null);
158            }
159        };
160        map.into()
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use crate::error::Result;
167    use crate::model::data_entry::DataEntry;
168    use crate::model::DataTransaction;
169    use crate::waves_proto::data_transaction_data::data_entry::Value::{
170        BinaryValue, BoolValue, IntValue, StringValue,
171    };
172    use crate::waves_proto::DataTransactionData;
173    use serde_json::{json, Map, Value};
174
175    use std::borrow::Borrow;
176    use std::fs;
177
178    #[test]
179    fn test_json_to_data_transaction() -> Result<()> {
180        let data = fs::read_to_string("./tests/resources/data_transaction_rs.json")
181            .expect("Unable to read file");
182        let json: Value = serde_json::from_str(&data).expect("failed to generate json from str");
183
184        let data_tx_from_json: DataTransaction = json.borrow().try_into()?;
185        let data_entries = data_tx_from_json.data();
186
187        for data_entry in data_entries {
188            match data_entry {
189                DataEntry::IntegerEntry { key, value } => {
190                    assert_eq!("int", key);
191                    assert_eq!(12, value);
192                }
193                DataEntry::BooleanEntry { key, value } => {
194                    assert_eq!("bool", key);
195                    assert_eq!(false, value);
196                }
197                DataEntry::BinaryEntry { key, value } => {
198                    assert_eq!("binary", key);
199                    assert_eq!([0_u8; 12].to_vec(), value);
200                }
201                DataEntry::StringEntry { key, value } => {
202                    assert_eq!("str", key);
203                    assert_eq!("value", value);
204                }
205                DataEntry::DeleteEntry { key } => {
206                    assert_eq!("del_str", key)
207                }
208            }
209        }
210
211        Ok(())
212    }
213
214    #[test]
215    fn test_data_transaction_to_proto() -> Result<()> {
216        let expected_vec_data = vec![
217            DataEntry::IntegerEntry {
218                key: "int".to_string(),
219                value: 12,
220            },
221            DataEntry::BooleanEntry {
222                key: "bool".to_string(),
223                value: false,
224            },
225            DataEntry::BinaryEntry {
226                key: "binary".to_string(),
227                value: [0; 12].to_vec(),
228            },
229            DataEntry::StringEntry {
230                key: "str".to_string(),
231                value: "value".to_string(),
232            },
233            DataEntry::DeleteEntry {
234                key: "del_str".to_string(),
235            },
236        ];
237        let data_transaction = &DataTransaction::new(expected_vec_data.clone());
238        let proto: DataTransactionData = data_transaction.try_into()?;
239        assert_eq!(expected_vec_data.len(), proto.data.len());
240
241        for data_entry in expected_vec_data {
242            match data_entry {
243                DataEntry::IntegerEntry { key, value } => {
244                    let int_entry = &proto.data[0];
245                    assert_eq!(int_entry.key, key);
246                    int_entry.value.clone().map(|it| match it {
247                        IntValue(int_value) => {
248                            assert_eq!(int_value, value)
249                        }
250                        _ => panic!("expected integer"),
251                    });
252                }
253                DataEntry::BooleanEntry { key, value } => {
254                    let bool_entry = &proto.data[1];
255                    assert_eq!(bool_entry.key, key);
256                    bool_entry.value.clone().map(|it| match it {
257                        BoolValue(bool_value) => {
258                            assert_eq!(bool_value, value)
259                        }
260                        _ => panic!("expected integer"),
261                    });
262                }
263                DataEntry::BinaryEntry { key, value } => {
264                    let binary_entry = &proto.data[2];
265                    assert_eq!(binary_entry.key, key);
266                    binary_entry.value.clone().map(|it| match it {
267                        BinaryValue(binary_value) => {
268                            assert_eq!(binary_value, value)
269                        }
270                        _ => panic!("expected integer"),
271                    });
272                }
273                DataEntry::StringEntry { key, value } => {
274                    let string_entry = &proto.data[3];
275                    assert_eq!(string_entry.key, key);
276                    string_entry.value.clone().map(|it| match it {
277                        StringValue(string_value) => {
278                            assert_eq!(string_value, value)
279                        }
280                        _ => panic!("expected integer"),
281                    });
282                }
283                DataEntry::DeleteEntry { key } => {
284                    let delete_entry = &proto.data[4];
285                    assert_eq!(delete_entry.key, key);
286                }
287            }
288        }
289
290        Ok(())
291    }
292
293    #[test]
294    fn test_data_transaction_to_json() -> Result<()> {
295        let data_transaction = &DataTransaction::new(vec![
296            DataEntry::IntegerEntry {
297                key: "int".to_string(),
298                value: 12,
299            },
300            DataEntry::BooleanEntry {
301                key: "bool".to_string(),
302                value: false,
303            },
304            DataEntry::BinaryEntry {
305                key: "binary".to_string(),
306                value: [0; 12].to_vec(),
307            },
308            DataEntry::StringEntry {
309                key: "str".to_string(),
310                value: "value".to_string(),
311            },
312            DataEntry::DeleteEntry {
313                key: "del_str".to_string(),
314            },
315        ]);
316
317        let map: Map<String, Value> = data_transaction.try_into()?;
318        let json: Value = map.into();
319        let expected_json = json!({"data": [
320                {
321                  "key": "int",
322                  "type": "integer",
323                  "value": 12
324                },
325                {
326                  "key": "bool",
327                  "type": "boolean",
328                  "value": false
329                },
330                {
331                  "key": "binary",
332                  "type": "binary",
333                  "value": "base64:AAAAAAAAAAAAAAAA"
334                },
335                {
336                  "key": "str",
337                  "type": "string",
338                  "value": "value"
339                },
340                {
341                  "key": "del_str",
342                  "value": null
343                }
344        ]});
345        assert_eq!(expected_json, json);
346        Ok(())
347    }
348}