kvptree/
lib.rs

1use anyhow::Result;
2use std::collections::HashMap;
3use std::fmt::Display;
4
5#[derive(Debug, Clone)]
6pub enum ValueType {
7    STRING(String),
8    LIST(HashMap<String, ValueType>),
9}
10
11impl PartialEq for ValueType {
12    fn eq(&self, other: &Self) -> bool {
13        match self {
14            ValueType::STRING(str1) => {
15                if let ValueType::STRING(str2) = other {
16                    str2 == str1
17                } else {
18                    false
19                }
20            }
21            ValueType::LIST(map1) => {
22                if let ValueType::LIST(map2) = other {
23                    map1 == map2
24                } else {
25                    false
26                }
27            }
28        }
29    }
30}
31
32impl ValueType {
33    pub fn get_str(&self, path: &str) -> Result<String> {
34        if path.is_empty() {
35            return if let ValueType::STRING(val) = self {
36                Ok(val.to_owned())
37            } else {
38                Err(anyhow::anyhow!(
39                    "error: query doesn't match the graph structure"
40                ))
41            };
42        }
43        let parts = path.split_once('.').unwrap_or((path, ""));
44
45        if let ValueType::LIST(map) = self {
46            let value = map.get(parts.0).ok_or(anyhow::anyhow!("error"))?;
47            value.get_str(parts.1)
48        } else {
49            Err(anyhow::anyhow!(
50                "error: query doesn't match the graph structure"
51            ))
52        }
53    }
54
55    ///always returns the list type but it is kept wrapped in `ValueType` so you can still use the .get_str and .get_node methods
56    pub fn get_node(&self, path: &str) -> Result<ValueType> {
57        if path.is_empty() {
58            return if let ValueType::LIST(_) = self {
59                Ok(self.clone())
60            } else {
61                Err(anyhow::anyhow!(
62                    "error: query doesn't match the graph structure"
63                ))
64            };
65        }
66        let parts = path.split_once('.').unwrap_or((path, ""));
67
68        if let ValueType::LIST(map) = self {
69            let value = map.get(parts.0).ok_or(anyhow::anyhow!("error"))?;
70            value.get_node(parts.1)
71        } else {
72            Err(anyhow::anyhow!(
73                "error: query doesn't match the graph structure"
74            ))
75        }
76    }
77}
78
79pub fn from_byte_vec(data: Vec<u8>) -> Result<ValueType> {
80    let data = String::from_utf8(data)?;
81    let data: Vec<String> = split_string(data);
82    let (_, map) = parse_list(data.get(1..).unwrap())?;
83
84    Ok(ValueType::LIST(map))
85}
86
87fn split_string(mut val: String) -> Vec<String> {
88    let mut parts: Vec<String> = Vec::new();
89    while !val.is_empty() {
90        let (part, rest) = val.split_once(' ').unwrap_or((val.as_str(), ""));
91        if part == "[" || part == "]" {
92            parts.push(part.to_owned());
93            val = rest.to_owned();
94            continue;
95        }
96        let str_len = part.parse::<usize>().unwrap();
97        let (part, rest) = rest.split_at(str_len);
98        parts.push(part.to_owned());
99        val = rest.trim().to_owned();
100    }
101
102    parts
103}
104
105fn parse_list(data: &[String]) -> Result<(usize, HashMap<String, ValueType>)> {
106    let mut map = HashMap::new();
107    let mut index = 0;
108    while index < data.len() {
109        let key = data.get(index).unwrap().clone();
110        if key == "]" {
111            return Ok((index, map));
112        }
113        index += 1;
114        let value = data.get(index).unwrap().clone();
115        if value == "[" {
116            let (i, val) = parse_list(data.get(index + 1..).unwrap())?;
117            map.insert(key.to_owned(), ValueType::LIST(val));
118            index += 1 + i;
119        } else {
120            map.insert(key.to_owned(), ValueType::STRING(value.to_owned()));
121        }
122        index += 1;
123    }
124
125    Ok((index, map))
126}
127
128pub fn to_byte_vec(data: ValueType) -> Vec<u8> {
129    if let ValueType::LIST(map) = data {
130        let string = write_list(map);
131        string.into_bytes()
132    } else {
133        panic!("Root ValueType must be a LIST variant")
134    }
135}
136
137fn write_list(map: HashMap<String, ValueType>) -> String {
138    let mut string = "[".to_owned();
139    for element in map {
140        string.push_str(&format!(" {} {} ", element.0.len(), element.0));
141        match element.1 {
142            ValueType::STRING(value) => {
143                string.push_str(&format!("{} {}", value.len(), value));
144            }
145            ValueType::LIST(map) => string.push_str(&write_list(map)),
146        }
147    }
148    string.push_str(" ]");
149    string
150}
151
152fn write_tree(node: ValueType, f: &mut std::fmt::Formatter<'_>, depth: usize) -> std::fmt::Result {
153    match node {
154        ValueType::STRING(val) => writeln!(f, "{}", val),
155        ValueType::LIST(map) => {
156            writeln!(f, "[")?;
157            for (key, value) in map {
158                for _ in 0..depth {
159                    write!(f, "  ")?;
160                }
161                write!(f, "  {} ", key)?;
162                write_tree(value, f, depth + 1)?;
163            }
164            for _ in 0..depth {
165                write!(f, "  ")?;
166            }
167            writeln!(f, "]")
168        }
169    }
170}
171
172impl Display for ValueType {
173    //make this nicer
174    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175        write_tree(self.clone(), f, 0)
176    }
177}