ros_core_rs/
param_tree.rs1use std::{collections::HashMap, mem};
2
3use dxr::{TryFromValue, TryToValue, Value};
4
5#[derive(Debug, PartialEq)]
6pub enum ParamValue {
7 HashMap(HashMap<String, ParamValue>),
8 Array(Vec<ParamValue>),
9 Value(Value),
10}
11
12impl From<&Value> for ParamValue {
13 fn from(value: &Value) -> Self {
14 if let Ok(hm) = HashMap::<String, Value>::try_from_value(value) {
15 let mut rv = HashMap::with_capacity(hm.len());
16 for (k, v) in hm.into_iter() {
17 rv.insert(k, ParamValue::from(&v));
18 }
19 return Self::HashMap(rv);
20 }
21 if let Ok(vec) = Vec::<Value>::try_from_value(value) {
22 let mut rv = Vec::with_capacity(vec.len());
23 for e in vec.into_iter() {
24 rv.push(ParamValue::from(&e))
25 }
26 return Self::Array(rv);
27 }
28 Self::Value(value.clone())
29 }
30}
31
32impl TryToValue for ParamValue {
33 fn try_to_value(&self) -> Result<Value, dxr::DxrError> {
34 match self {
35 ParamValue::Value(v) => Ok(v.clone()),
36 ParamValue::Array(arr) => arr.try_to_value(),
37 ParamValue::HashMap(hm) => hm.try_to_value(),
38 }
39 }
40}
41
42impl ParamValue {
43 pub(crate) fn get_keys(&self) -> Vec<String> {
44 match self {
45 ParamValue::HashMap(hm) => {
46 let mut keys = Vec::new();
47 for (k, v) in hm.iter() {
48 keys.push(format!("/{k}"));
49 for suffix in v.get_keys() {
50 keys.push(format!("/{k}{suffix}"));
51 }
52 }
53 keys
54 }
55 _ => Vec::new(),
56 }
57 }
58 pub(crate) fn get<I, T>(&self, key: I) -> Option<Value>
59 where
60 I: IntoIterator<Item = T>,
61 T: AsRef<str>,
62 {
63 let mut hm = self;
64 for e in key.into_iter() {
65 let e = e.as_ref();
66 if e == "" {
67 continue;
68 }
69 match hm {
70 ParamValue::HashMap(inner) => {
71 if let Some(inner_value) = inner.get(e) {
72 hm = inner_value;
73 } else {
74 return None;
75 }
76 }
77 _ => return None,
78 }
79 }
80 Some(hm.try_to_value().unwrap())
81 }
82
83 pub(crate) fn remove<I, T>(&mut self, key: I)
84 where
85 I: IntoIterator<Item = T>,
86 T: AsRef<str>,
87 {
88 let mut peekable = key.into_iter().peekable();
89 match self {
90 ParamValue::HashMap(inner) => {
91 let mut hm = inner;
92 loop {
93 let current_key = peekable.next();
94 let next_key = peekable.peek();
95 match (current_key, next_key) {
96 (Some(current_key), None) => {
97 hm.remove(current_key.as_ref());
98 return;
99 }
100 (None, None) => {
101 let _ = mem::replace(self, ParamValue::HashMap(hashmap! {}));
102 return;
103 }
104 (None, Some(_)) => unreachable!(),
105 (Some(current_key), Some(_)) => match hm.get_mut(current_key.as_ref()) {
106 Some(ParamValue::HashMap(new_hm)) => hm = new_hm,
107 _ => return,
108 },
109 }
110 }
111 }
112 _ => (),
113 }
114 }
115
116 pub(crate) fn update_inner<I, T>(&mut self, mut key: I, value: Value)
117 where
118 I: Iterator<Item = T>,
119 T: AsRef<str>,
120 {
121 match key.next() {
122 None => {
123 let _ = mem::replace(self, ParamValue::from(&value));
124 }
125 Some(next_key) => match self {
126 ParamValue::HashMap(hm) => match hm.get_mut(next_key.as_ref()) {
127 Some(inner) => inner.update_inner(key, value),
128 None => {
129 hm.insert(next_key.as_ref().to_string(), {
130 let mut inner = ParamValue::HashMap(HashMap::new());
131 inner.update_inner(key, value);
132 inner
133 });
134 }
135 },
136 _ => {
137 let mut inner = ParamValue::HashMap(hashmap! {});
138 inner.update_inner(key, value);
139 let outer = ParamValue::HashMap(hashmap! {
140 next_key.as_ref().to_string() => inner
141 });
142 let _ = mem::replace(self, outer);
143 }
144 },
145 }
146 }
147}
148
149use maplit::hashmap;
150
151#[test]
152fn test_param_tree() {
153 let mut tree = ParamValue::HashMap(hashmap! {
154 "run_id".to_owned() => ParamValue::Value(Value::string("asdf-jkl0".to_owned())),
155 "robot_id".to_owned() => ParamValue::Value(Value::i4(42)),
156 "robot_configs".to_owned() => ParamValue::Array(vec![
157 ParamValue::HashMap(hashmap! {
158 "robot_speed".to_owned() => ParamValue::Value(Value::double(3.0)),
159 "robot_id".to_owned() => ParamValue::Value(Value::i4(24))
160 })
161 ]),
162 "arms".to_owned() => ParamValue::HashMap(hashmap! {
163 "arm_left".to_owned() => ParamValue::HashMap(hashmap! {
164 "length".to_owned() => ParamValue::Value(Value::double(-0.45))
165 })
166 })
167 });
168
169 tree.update_inner(["robot_configs"].iter(), Value::i4(23));
170 let res = tree.get(["robot_configs"]).unwrap();
171 assert_eq!(res, Value::i4(23));
172}