dpp/util/cbor_value/
value.rs

1use std::convert::{TryFrom, TryInto};
2
3use ciborium::value::Value;
4use itertools::Itertools;
5
6use crate::util::{
7    cbor_value::convert::convert_to,
8    json_path::{JsonPath, JsonPathLiteral, JsonPathStep},
9};
10
11use super::{FieldType, ReplacePaths, ValuesCollection};
12
13impl ValuesCollection for ciborium::value::Value {
14    type Value = ciborium::value::Value;
15    type Key = ciborium::value::Value;
16
17    fn get(&self, key: &Self::Value) -> Option<&Self::Value> {
18        match self {
19            Value::Array(ref arr) => {
20                if let Some(idx) = key.as_integer() {
21                    let idx: usize = idx.try_into().ok()?;
22                    arr.get(idx)
23                } else {
24                    None
25                }
26            }
27            Value::Map(map) => map
28                .iter()
29                .find_map(|(k, v)| if k == key { Some(v) } else { None }),
30
31            _ => None,
32        }
33    }
34
35    fn get_mut(&mut self, key: &Self::Value) -> Option<&mut Self::Value> {
36        match self {
37            Value::Array(ref mut arr) => {
38                if let Some(idx) = key.as_integer() {
39                    let idx: usize = idx.try_into().ok()?;
40                    arr.get_mut(idx)
41                } else {
42                    None
43                }
44            }
45            Value::Map(map) => map
46                .iter_mut()
47                .find_map(|(k, v)| if k == key { Some(v) } else { None }),
48
49            _ => None,
50        }
51    }
52
53    fn remove(&mut self, key: impl Into<Self::Key>) -> Option<Self::Value> {
54        let key_cbor: Self::Key = key.into();
55        match self {
56            Value::Array(ref mut arr) => {
57                if let Some(idx) = key_cbor.as_integer() {
58                    let idx: usize = idx.try_into().ok()?;
59                    if arr.len() < idx {
60                        return Some(arr.remove(idx));
61                    }
62                }
63                None
64            }
65            Value::Map(map) => {
66                if let Some(idx) = map.iter().position(|(el_key, _)| el_key == &key_cbor) {
67                    let (_, v) = map.remove(idx);
68                    return Some(v);
69                }
70                None
71            }
72            _ => None,
73        }
74    }
75}
76
77impl ReplacePaths for ciborium::value::Value {
78    type Value = ciborium::value::Value;
79
80    fn replace_paths<I, C>(&mut self, paths: I, from: FieldType, to: FieldType)
81    where
82        I: IntoIterator<Item = C>,
83        C: AsRef<str>,
84    {
85        for path in paths.into_iter() {
86            self.replace_path(path.as_ref(), from, to);
87        }
88    }
89
90    fn replace_path(&mut self, path: &str, from: FieldType, to: FieldType) -> Option<()> {
91        let cbor_value = self.get_path_mut(path)?;
92        let replace_with = convert_to(cbor_value, from, to)?;
93
94        *cbor_value = replace_with;
95
96        Some(())
97    }
98
99    fn get_path_mut(&mut self, path: &str) -> Option<&mut Value> {
100        let cbor_path = to_path_of_cbors(path).ok()?;
101
102        if cbor_path.is_empty() {
103            return None;
104        }
105        if cbor_path.len() == 1 {
106            return self.get_mut(&cbor_path[0]);
107        }
108
109        let mut current_level: &mut Value = self.get_mut(&cbor_path[0])?;
110        for step in cbor_path.iter().skip(1) {
111            match current_level {
112                Value::Map(ref mut cbor_map) => current_level = get_from_cbor_map(cbor_map, step)?,
113                Value::Array(ref mut cbor_array) => {
114                    if let Some(idx) = step.as_integer() {
115                        let id: usize = idx.try_into().ok()?;
116                        current_level = cbor_array.get_mut(id)?
117                    } else {
118                        return None;
119                    }
120                }
121                _ => {
122                    // do nothing if it's not a container type
123                }
124            }
125        }
126        Some(current_level)
127    }
128}
129
130pub fn get_from_cbor_map<'a>(
131    cbor_map: &'a mut [(Value, Value)],
132    key: &Value,
133) -> Option<&'a mut Value> {
134    cbor_map.iter_mut().find_map(|(current_key, value)| {
135        if current_key == key {
136            Some(value)
137        } else {
138            None
139        }
140    })
141}
142
143pub fn to_path_of_cbors(path: &str) -> Result<Vec<Value>, anyhow::Error> {
144    let json_path = JsonPath::try_from(JsonPathLiteral(path))?;
145
146    Ok(json_path
147        .into_iter()
148        .map(|step| match step {
149            JsonPathStep::Key(key) => Value::Text(key),
150            JsonPathStep::Index(index) => Value::Integer(index.into()),
151        })
152        .collect_vec())
153}