ros_core_rs/
param_tree.rs

1use 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}