Skip to main content

dpp/util/cbor_value/
mod.rs

1use std::convert::TryInto;
2
3use anyhow::anyhow;
4use ciborium::value::Value as CborValue;
5use serde_json::{Map, Value as JsonValue};
6
7use crate::ProtocolError;
8mod convert;
9pub use convert::FieldType;
10
11mod value;
12pub use value::*;
13
14mod canonical;
15pub use canonical::*;
16
17mod map;
18pub use map::*;
19
20pub trait ValuesCollection {
21    type Key;
22    type Value;
23
24    fn get_mut(&mut self, key: &Self::Key) -> Option<&mut Self::Value>;
25    fn get(&self, key: &Self::Key) -> Option<&Self::Value>;
26    fn remove(&mut self, key_to_remove: impl Into<Self::Key>) -> Option<Self::Value>;
27}
28
29pub trait ReplacePaths: ValuesCollection {
30    type Value;
31
32    fn replace_paths<I, C>(&mut self, paths: I, from: FieldType, to: FieldType)
33    where
34        I: IntoIterator<Item = C>,
35        C: AsRef<str>;
36
37    fn replace_path(&mut self, path: &str, from: FieldType, to: FieldType) -> Option<()>;
38    fn get_path_mut(&mut self, path: &str) -> Option<&mut <Self as ReplacePaths>::Value>;
39}
40
41pub fn get_key_from_cbor_map<'a>(
42    cbor_map: &'a [(CborValue, CborValue)],
43    key: &'a str,
44) -> Option<&'a CborValue> {
45    for (cbor_key, cbor_value) in cbor_map.iter() {
46        if !cbor_key.is_text() {
47            continue;
48        }
49
50        if cbor_key.as_text().expect("confirmed as text") == key {
51            return Some(cbor_value);
52        }
53    }
54    None
55}
56
57impl CborMapExtension for &Vec<(CborValue, CborValue)> {
58    fn as_u16(&self, key: &str, error_message: &str) -> Result<u16, ProtocolError> {
59        let key_value = get_key_from_cbor_map(self, key)
60            .ok_or_else(|| ProtocolError::DecodingError(String::from(error_message)))?;
61        if let CborValue::Integer(integer_value) = key_value {
62            return Ok(i128::from(*integer_value) as u16);
63        }
64        Err(ProtocolError::DecodingError(String::from(error_message)))
65    }
66
67    fn as_u8(&self, key: &str, error_message: &str) -> Result<u8, ProtocolError> {
68        let key_value = get_key_from_cbor_map(self, key)
69            .ok_or_else(|| ProtocolError::DecodingError(String::from(error_message)))?;
70        if let CborValue::Integer(integer_value) = key_value {
71            return Ok(i128::from(*integer_value) as u8);
72        }
73        Err(ProtocolError::DecodingError(String::from(error_message)))
74    }
75
76    fn as_bool(&self, key: &str, error_message: &str) -> Result<bool, ProtocolError> {
77        let key_value = get_key_from_cbor_map(self, key)
78            .ok_or_else(|| ProtocolError::DecodingError(String::from(error_message)))?;
79        if let CborValue::Bool(bool_value) = key_value {
80            return Ok(*bool_value);
81        }
82        Err(ProtocolError::DecodingError(String::from(error_message)))
83    }
84
85    fn as_bytes(&self, key: &str, error_message: &str) -> Result<Vec<u8>, ProtocolError> {
86        let key_value = get_key_from_cbor_map(self, key)
87            .ok_or_else(|| ProtocolError::DecodingError(String::from(error_message)))?;
88        match key_value {
89            CborValue::Bytes(bytes) => Ok(bytes.clone()),
90            CborValue::Array(array) => array
91                .iter()
92                .map(|byte| match byte {
93                    CborValue::Integer(int) => {
94                        let value_as_u8: u8 = (*int).try_into().map_err(|_| {
95                            ProtocolError::DecodingError(String::from("expected u8 value"))
96                        })?;
97                        Ok(value_as_u8)
98                    }
99                    _ => Err(ProtocolError::DecodingError(String::from(
100                        "not an array of integers",
101                    ))),
102                })
103                .collect::<Result<Vec<u8>, ProtocolError>>(),
104            _ => Err(ProtocolError::DecodingError(String::from(error_message))),
105        }
106    }
107
108    fn as_string(&self, key: &str, error_message: &str) -> Result<String, ProtocolError> {
109        let key_value = get_key_from_cbor_map(self, key)
110            .ok_or_else(|| ProtocolError::DecodingError(String::from(error_message)))?;
111        if let CborValue::Text(string_value) = key_value {
112            return Ok(string_value.clone());
113        }
114        Err(ProtocolError::DecodingError(String::from(error_message)))
115    }
116
117    fn as_u64(&self, key: &str, error_message: &str) -> Result<u64, ProtocolError> {
118        let key_value = get_key_from_cbor_map(self, key)
119            .ok_or_else(|| ProtocolError::DecodingError(String::from(error_message)))?;
120        if let CborValue::Integer(integer_value) = key_value {
121            return Ok(i128::from(*integer_value) as u64);
122        }
123        Err(ProtocolError::DecodingError(String::from(error_message)))
124    }
125}
126
127// TODO: the issue with stack overflow should be address through re-implementation of the algorithm
128pub fn cbor_value_to_json_value(cbor: &CborValue) -> Result<serde_json::Value, anyhow::Error> {
129    match cbor {
130        CborValue::Integer(num) => Ok(JsonValue::from(i128::from(*num) as i64)),
131        CborValue::Bytes(bytes) => Ok(JsonValue::Array(
132            bytes.iter().map(|byte| JsonValue::from(*byte)).collect(),
133        )),
134        CborValue::Float(float) => Ok(JsonValue::from(*float)),
135        CborValue::Text(text) => Ok(JsonValue::from(text.clone())),
136        CborValue::Bool(boolean) => Ok(JsonValue::from(*boolean)),
137        CborValue::Null => Ok(JsonValue::Null),
138        CborValue::Array(arr) => Ok(JsonValue::Array(
139            arr.iter()
140                .map(cbor_value_to_json_value)
141                .collect::<Result<Vec<JsonValue>, anyhow::Error>>()?,
142        )),
143        CborValue::Map(map) => cbor_map_to_json_map(map),
144        _ => Err(anyhow!("Can't convert CBOR to JSON: unknown type")),
145    }
146}
147
148pub fn cbor_value_into_json_value(cbor: CborValue) -> Result<serde_json::Value, anyhow::Error> {
149    match cbor {
150        CborValue::Integer(num) => Ok(JsonValue::from(i128::from(num) as i64)),
151        CborValue::Bytes(bytes) => Ok(JsonValue::Array(
152            bytes.into_iter().map(JsonValue::from).collect(),
153        )),
154        CborValue::Float(float) => Ok(JsonValue::from(float)),
155        CborValue::Text(text) => Ok(JsonValue::from(text)),
156        CborValue::Bool(boolean) => Ok(JsonValue::from(boolean)),
157        CborValue::Null => Ok(JsonValue::Null),
158        CborValue::Array(arr) => Ok(JsonValue::Array(
159            arr.into_iter()
160                .map(cbor_value_into_json_value)
161                .collect::<Result<Vec<JsonValue>, anyhow::Error>>()?,
162        )),
163        CborValue::Map(map) => cbor_map_into_json_map(map),
164        _ => Err(anyhow!("Can't convert CBOR to JSON: unknown type")),
165    }
166}
167
168pub fn cbor_map_to_json_map(
169    cbor_map: &[(CborValue, CborValue)],
170) -> Result<serde_json::Value, anyhow::Error> {
171    let mut json_vec = cbor_map
172        .iter()
173        .map(|(key, value)| {
174            Ok((
175                key.as_text()
176                    .ok_or_else(|| anyhow!("Expect key to be a string"))?
177                    .to_string(),
178                cbor_value_to_json_value(value)?,
179            ))
180        })
181        .collect::<Result<Vec<(String, JsonValue)>, anyhow::Error>>()?;
182
183    let mut json_map = Map::new();
184
185    for (key, value) in json_vec.drain(..) {
186        json_map.insert(key, value);
187    }
188
189    Ok(serde_json::Value::Object(json_map))
190}
191
192pub fn cbor_map_into_json_map(
193    cbor_map: Vec<(CborValue, CborValue)>,
194) -> Result<serde_json::Value, anyhow::Error> {
195    let mut json_vec = cbor_map
196        .into_iter()
197        .map(|(key, value)| {
198            Ok((
199                key.into_text()
200                    .map_err(|_| anyhow!("Expect key to be a string"))?,
201                cbor_value_into_json_value(value)?,
202            ))
203        })
204        .collect::<Result<Vec<(String, JsonValue)>, anyhow::Error>>()?;
205
206    let mut json_map = Map::new();
207
208    for (key, value) in json_vec.drain(..) {
209        json_map.insert(key, value);
210    }
211
212    Ok(serde_json::Value::Object(json_map))
213}
214
215#[cfg(test)]
216#[allow(clippy::approx_constant)]
217mod tests {
218    use super::*;
219    use ciborium::value::Value as CborValue;
220    use serde_json::json;
221
222    // --- get_key_from_cbor_map ---
223
224    #[test]
225    fn get_key_from_cbor_map_found() {
226        let map = vec![
227            (
228                CborValue::Text("name".to_string()),
229                CborValue::Text("Alice".to_string()),
230            ),
231            (
232                CborValue::Text("age".to_string()),
233                CborValue::Integer(30.into()),
234            ),
235        ];
236        let result = get_key_from_cbor_map(&map, "name");
237        assert_eq!(result, Some(&CborValue::Text("Alice".to_string())));
238    }
239
240    #[test]
241    fn get_key_from_cbor_map_not_found() {
242        let map = vec![(
243            CborValue::Text("name".to_string()),
244            CborValue::Text("Alice".to_string()),
245        )];
246        let result = get_key_from_cbor_map(&map, "missing");
247        assert!(result.is_none());
248    }
249
250    #[test]
251    fn get_key_from_cbor_map_skips_non_text_keys() {
252        let map = vec![
253            (CborValue::Integer(1.into()), CborValue::Bool(true)),
254            (CborValue::Text("key".to_string()), CborValue::Bool(false)),
255        ];
256        let result = get_key_from_cbor_map(&map, "key");
257        assert_eq!(result, Some(&CborValue::Bool(false)));
258    }
259
260    // --- CborMapExtension for &Vec<(CborValue, CborValue)> ---
261
262    fn make_cbor_map(pairs: Vec<(&str, CborValue)>) -> Vec<(CborValue, CborValue)> {
263        pairs
264            .into_iter()
265            .map(|(k, v)| (CborValue::Text(k.to_string()), v))
266            .collect()
267    }
268
269    #[test]
270    fn cbor_map_extension_as_u16() {
271        let map = make_cbor_map(vec![("val", CborValue::Integer(1234.into()))]);
272        let result = (&map).as_u16("val", "err");
273        assert_eq!(result.unwrap(), 1234);
274    }
275
276    #[test]
277    fn cbor_map_extension_as_u16_missing() {
278        let map = make_cbor_map(vec![]);
279        let result = (&map).as_u16("val", "missing");
280        assert!(result.is_err());
281    }
282
283    #[test]
284    fn cbor_map_extension_as_u16_wrong_type() {
285        let map = make_cbor_map(vec![("val", CborValue::Text("hello".to_string()))]);
286        let result = (&map).as_u16("val", "err");
287        assert!(result.is_err());
288    }
289
290    #[test]
291    fn cbor_map_extension_as_u8() {
292        let map = make_cbor_map(vec![("val", CborValue::Integer(255.into()))]);
293        let result = (&map).as_u8("val", "err");
294        assert_eq!(result.unwrap(), 255);
295    }
296
297    #[test]
298    fn cbor_map_extension_as_bool() {
299        let map = make_cbor_map(vec![("flag", CborValue::Bool(true))]);
300        let result = (&map).as_bool("flag", "err");
301        assert!(result.unwrap());
302    }
303
304    #[test]
305    fn cbor_map_extension_as_bool_missing() {
306        let map = make_cbor_map(vec![]);
307        let result = (&map).as_bool("flag", "missing");
308        assert!(result.is_err());
309    }
310
311    #[test]
312    fn cbor_map_extension_as_bool_wrong_type() {
313        let map = make_cbor_map(vec![("flag", CborValue::Integer(1.into()))]);
314        let result = (&map).as_bool("flag", "err");
315        assert!(result.is_err());
316    }
317
318    #[test]
319    fn cbor_map_extension_as_bytes_from_bytes() {
320        let data = vec![1u8, 2, 3, 4];
321        let map = make_cbor_map(vec![("data", CborValue::Bytes(data.clone()))]);
322        let result = (&map).as_bytes("data", "err");
323        assert_eq!(result.unwrap(), data);
324    }
325
326    #[test]
327    fn cbor_map_extension_as_bytes_from_array() {
328        let array = vec![CborValue::Integer(10.into()), CborValue::Integer(20.into())];
329        let map = make_cbor_map(vec![("data", CborValue::Array(array))]);
330        let result = (&map).as_bytes("data", "err");
331        assert_eq!(result.unwrap(), vec![10u8, 20]);
332    }
333
334    #[test]
335    fn cbor_map_extension_as_bytes_wrong_type() {
336        let map = make_cbor_map(vec![("data", CborValue::Bool(true))]);
337        let result = (&map).as_bytes("data", "err");
338        assert!(result.is_err());
339    }
340
341    #[test]
342    fn cbor_map_extension_as_string() {
343        let map = make_cbor_map(vec![("s", CborValue::Text("hello".to_string()))]);
344        let result = (&map).as_string("s", "err");
345        assert_eq!(result.unwrap(), "hello");
346    }
347
348    #[test]
349    fn cbor_map_extension_as_string_wrong_type() {
350        let map = make_cbor_map(vec![("s", CborValue::Integer(1.into()))]);
351        let result = (&map).as_string("s", "err");
352        assert!(result.is_err());
353    }
354
355    #[test]
356    fn cbor_map_extension_as_u64() {
357        let map = make_cbor_map(vec![("n", CborValue::Integer(999999.into()))]);
358        let result = (&map).as_u64("n", "err");
359        assert_eq!(result.unwrap(), 999999);
360    }
361
362    // --- cbor_value_to_json_value ---
363
364    #[test]
365    fn cbor_integer_to_json() {
366        let result = cbor_value_to_json_value(&CborValue::Integer(42.into())).unwrap();
367        assert_eq!(result, json!(42));
368    }
369
370    #[test]
371    fn cbor_text_to_json() {
372        let result = cbor_value_to_json_value(&CborValue::Text("hello".to_string())).unwrap();
373        assert_eq!(result, json!("hello"));
374    }
375
376    #[test]
377    fn cbor_bool_to_json() {
378        let result = cbor_value_to_json_value(&CborValue::Bool(true)).unwrap();
379        assert_eq!(result, json!(true));
380    }
381
382    #[test]
383    fn cbor_null_to_json() {
384        let result = cbor_value_to_json_value(&CborValue::Null).unwrap();
385        assert!(result.is_null());
386    }
387
388    #[test]
389    fn cbor_float_to_json() {
390        let result = cbor_value_to_json_value(&CborValue::Float(3.14)).unwrap();
391        assert_eq!(result, json!(3.14));
392    }
393
394    #[test]
395    fn cbor_bytes_to_json_array() {
396        let result = cbor_value_to_json_value(&CborValue::Bytes(vec![1, 2, 3])).unwrap();
397        assert_eq!(result, json!([1, 2, 3]));
398    }
399
400    #[test]
401    fn cbor_array_to_json_array() {
402        let cbor = CborValue::Array(vec![
403            CborValue::Integer(1.into()),
404            CborValue::Text("two".to_string()),
405        ]);
406        let result = cbor_value_to_json_value(&cbor).unwrap();
407        assert_eq!(result, json!([1, "two"]));
408    }
409
410    #[test]
411    fn cbor_map_to_json_object() {
412        let cbor = CborValue::Map(vec![(
413            CborValue::Text("key".to_string()),
414            CborValue::Integer(10.into()),
415        )]);
416        let result = cbor_value_to_json_value(&cbor).unwrap();
417        assert_eq!(result, json!({"key": 10}));
418    }
419
420    // --- cbor_value_into_json_value (owned) ---
421
422    #[test]
423    fn cbor_into_json_integer() {
424        let result = cbor_value_into_json_value(CborValue::Integer(99.into())).unwrap();
425        assert_eq!(result, json!(99));
426    }
427
428    #[test]
429    fn cbor_into_json_text() {
430        let result = cbor_value_into_json_value(CborValue::Text("world".to_string())).unwrap();
431        assert_eq!(result, json!("world"));
432    }
433
434    #[test]
435    fn cbor_into_json_null() {
436        let result = cbor_value_into_json_value(CborValue::Null).unwrap();
437        assert!(result.is_null());
438    }
439
440    #[test]
441    fn cbor_into_json_nested_array() {
442        let cbor = CborValue::Array(vec![CborValue::Array(vec![CborValue::Integer(1.into())])]);
443        let result = cbor_value_into_json_value(cbor).unwrap();
444        assert_eq!(result, json!([[1]]));
445    }
446
447    // --- cbor_map_to_json_map ---
448
449    #[test]
450    fn test_cbor_map_to_json_map_valid() {
451        let map = vec![
452            (
453                CborValue::Text("a".to_string()),
454                CborValue::Integer(1.into()),
455            ),
456            (CborValue::Text("b".to_string()), CborValue::Bool(false)),
457        ];
458        let result = cbor_map_to_json_map(&map).unwrap();
459        assert_eq!(result, json!({"a": 1, "b": false}));
460    }
461
462    #[test]
463    fn test_cbor_map_to_json_map_non_string_key() {
464        let map = vec![(CborValue::Integer(1.into()), CborValue::Bool(true))];
465        let result = cbor_map_to_json_map(&map);
466        assert!(result.is_err());
467    }
468
469    // --- cbor_map_into_json_map (owned) ---
470
471    #[test]
472    fn test_cbor_map_into_json_map_valid() {
473        let map = vec![(
474            CborValue::Text("x".to_string()),
475            CborValue::Text("y".to_string()),
476        )];
477        let result = cbor_map_into_json_map(map).unwrap();
478        assert_eq!(result, json!({"x": "y"}));
479    }
480
481    #[test]
482    fn test_cbor_map_into_json_map_non_string_key() {
483        let map = vec![(CborValue::Integer(1.into()), CborValue::Bool(true))];
484        let result = cbor_map_into_json_map(map);
485        assert!(result.is_err());
486    }
487}