Skip to main content

zql_cli/core/
case.rs

1use odbc_api::Nullability;
2use std::borrow::Cow;
3use std::collections::hash_map::Iter;
4use std::collections::HashMap;
5use std::ops::Deref;
6
7#[derive(Clone, Copy, PartialEq)]
8pub enum Entity {
9    Default, // (append "" after unique table or column name)
10    Procedure, // (append "(" after unique stored procedure)
11    Keyword, // (append " " after unique SQL keyword)
12    Function, // (append "(" after unique SQL function)
13}
14
15#[derive(Debug, PartialEq)]
16pub struct Payload {
17    pub dtype: String,
18    pub null: Nullability,
19    pub index: usize,
20}
21
22impl Payload {
23    pub fn new(
24        dtype: Cow<str>,
25        null: Nullability,
26        index: usize,
27    ) -> Self {
28        let dtype = dtype.into_owned();
29        Self { dtype, null, index }
30    }
31}
32
33pub struct CaseTree {
34    value: String,
35    entity: Entity,
36    payload: Option<Box<Payload>>,
37    children: Option<HashMap<String, CaseTree>>,
38}
39
40impl CaseTree {
41    pub fn new(value: Cow<str>) -> Self {
42        Self {
43            value: value.into_owned(),
44            entity: Entity::Default,
45            payload: None,
46            children: None,
47        }
48    }
49
50    pub fn set_entity(&mut self, entity: Entity) -> &mut Self {
51        self.entity = entity;
52        self
53    }
54
55    pub fn set_payload(
56        &mut self,
57        dtype: Cow<str>,
58        null: Nullability,
59        index: usize,
60    ) {
61        let payload = Payload::new(dtype, null, index);
62        self.payload = Some(Box::new(payload));
63    }
64
65    pub fn as_str(&self) -> &str {
66        self.value.as_str()
67    }
68
69    pub fn get_payload(&self) -> Option<&Payload> {
70        self.payload.as_ref().map(Box::deref)
71    }
72
73    pub fn insert_child(&mut self, path: &[Cow<str>], value: Cow<str>) -> Option<&mut Self> {
74        let mut path = path;
75        while let [head, tail @ ..] = path {
76            if !head.is_empty() {
77                let child = self.children
78                    .get_or_insert_with(HashMap::new)
79                    .entry(head.to_lowercase())
80                    .or_insert_with(|| Self::new(head.clone()));
81                return child.insert_child(tail, value);
82            }
83            path = tail;
84        }
85        if !value.is_empty() {
86            let child = self.children
87                .get_or_insert_with(HashMap::new)
88                .entry(value.to_lowercase())
89                .or_insert_with(|| Self::new(value));
90            return Some(child);
91        }
92        None
93    }
94
95    pub fn get_child(&self, path: &[&str]) -> Option<&Self> {
96        if let [head, tail @ ..] = path {
97            if head.is_empty() {
98                return self.get_child(tail);
99            }
100            if let Some(children) = &self.children {
101                if let Some(child) = children.get(*head) {
102                    return child.get_child(tail);
103                }
104            }
105            None
106        } else {
107            Some(self)
108        }
109    }
110
111    pub fn get_values<'a>(&'a self, path: &[&str]) -> CaseValues<'a> {
112        if let Some(child) = self.get_child(path) {
113            if let Some(children) = &child.children {
114                return CaseValues::new(children);
115            }
116        }
117        CaseValues::default()
118    }
119
120    pub fn get_nodes(&self, entity: Entity) -> CaseNodes<'_> {
121        if let Some(children) = &self.children {
122            return CaseNodes::new(children, entity);
123        }
124        CaseNodes::default()
125    }
126}
127
128pub struct CaseValues<'a> {
129    inner: Iter<'a, String, CaseTree>,
130}
131
132impl<'a> CaseValues<'a> {
133    pub fn new(children: &'a HashMap<String, CaseTree>) -> Self {
134        let inner = children.iter();
135        Self { inner }
136    }
137}
138
139impl<'a> Default for CaseValues<'a> {
140    fn default() -> Self {
141        let inner = Iter::default();
142        Self { inner }
143    }
144}
145
146impl<'a> Iterator for CaseValues<'a> {
147    type Item = (&'a String, (&'a String, Entity));
148
149    fn next(&mut self) -> Option<Self::Item> {
150        if let Some((key, child)) = self.inner.next() {
151            Some((key, (&child.value, child.entity)))
152        } else {
153            None
154        }
155    }
156}
157
158pub struct CaseNodes<'a> {
159    inner: Iter<'a, String, CaseTree>,
160    entity: Entity,
161}
162
163impl<'a> CaseNodes<'a> {
164    pub fn new(children: &'a HashMap<String, CaseTree>, entity: Entity) -> Self {
165        let inner = children.iter();
166        Self { inner, entity }
167    }
168}
169
170impl<'a> Default for CaseNodes<'a> {
171    fn default() -> Self {
172        let inner = Iter::default();
173        let entity = Entity::Default;
174        Self { inner, entity }
175    }
176}
177
178impl<'a> Iterator for CaseNodes<'a> {
179    type Item = (&'a String, &'a CaseTree);
180
181    fn next(&mut self) -> Option<Self::Item> {
182        while let Some((key, child)) = self.inner.next() {
183            if child.entity == self.entity {
184                return Some((key, child));
185            }
186        }
187        None
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    use crate::core::case::{CaseTree, Entity};
194    use crate::{cow_arr, cow_str, str_vec};
195    use pretty_assertions::assert_eq;
196    use std::borrow::Cow;
197    use std::collections::BTreeMap;
198
199    #[test]
200    fn test_leaf_not_inserted_with_empty_path_and_empty_value() {
201        let mut tree = CaseTree::new(cow_str!(""));
202        tree.insert_child(&[], cow_str!(""));
203        assert_eq!(get_children(&tree, &[]), None);
204        assert_eq!(get_children(&tree, &["alpha"]), None);
205        assert_eq!(get_children(&tree, &["alpha", "delta"]), None);
206        assert_eq!(get_children(&tree, &["alpha", "delta", "kilo"]), None);
207    }
208
209    #[test]
210    fn test_leaf_is_inserted_with_empty_path_and_valid_value() {
211        let mut tree = CaseTree::new(cow_str!(""));
212        tree.insert_child(&[], cow_str!("Alpha"));
213        assert_eq!(get_children(&tree, &[]), Some(str_vec!["Alpha"]));
214        assert_eq!(get_children(&tree, &["alpha"]), None);
215        assert_eq!(get_children(&tree, &["alpha", "delta"]), None);
216        assert_eq!(get_children(&tree, &["alpha", "delta", "kilo"]), None);
217    }
218
219    #[test]
220    fn test_leaf_not_inserted_with_compact_path_and_empty_value() {
221        let mut tree = CaseTree::new(cow_str!(""));
222        tree.insert_child(cow_arr!["Alpha", "Delta"], cow_str!(""));
223        assert_eq!(get_children(&tree, &[]), Some(str_vec!["Alpha"]));
224        assert_eq!(get_children(&tree, &["alpha"]), Some(str_vec!["Delta"]));
225        assert_eq!(get_children(&tree, &["alpha", "delta"]), None);
226        assert_eq!(get_children(&tree, &["alpha", "delta", "kilo"]), None);
227    }
228
229    #[test]
230    fn test_leaf_is_inserted_with_compact_path_and_valid_value() {
231        let mut tree = CaseTree::new(cow_str!(""));
232        tree.insert_child(cow_arr!["Alpha", "Delta"], cow_str!("Kilo"));
233        assert_eq!(get_children(&tree, &[]), Some(str_vec!["Alpha"]));
234        assert_eq!(get_children(&tree, &["alpha"]), Some(str_vec!["Delta"]));
235        assert_eq!(get_children(&tree, &["alpha", "delta"]), Some(str_vec!["Kilo"]));
236        assert_eq!(get_children(&tree, &["alpha", "delta", "kilo"]), None);
237    }
238
239    #[test]
240    fn test_leaf_not_inserted_with_sparse_path_and_empty_value() {
241        let mut tree = CaseTree::new(cow_str!(""));
242        tree.insert_child(cow_arr!["", "Alpha", "", "Delta", ""], cow_str!(""));
243        assert_eq!(get_children(&tree, &[]), Some(str_vec!["Alpha"]));
244        assert_eq!(get_children(&tree, &["alpha"]), Some(str_vec!["Delta"]));
245        assert_eq!(get_children(&tree, &["alpha", "delta"]), None);
246        assert_eq!(get_children(&tree, &["alpha", "delta", "kilo"]), None);
247    }
248
249    #[test]
250    fn test_leaf_is_inserted_with_sparse_path_and_valid_value() {
251        let mut tree = CaseTree::new(cow_str!(""));
252        tree.insert_child(cow_arr!["", "Alpha", "", "Delta", ""], cow_str!("Kilo"));
253        assert_eq!(get_children(&tree, &[]), Some(str_vec!["Alpha"]));
254        assert_eq!(get_children(&tree, &["alpha"]), Some(str_vec!["Delta"]));
255        assert_eq!(get_children(&tree, &["alpha", "delta"]), Some(str_vec!["Kilo"]));
256        assert_eq!(get_children(&tree, &["alpha", "delta", "kilo"]), None);
257    }
258
259    fn get_children(tree: &CaseTree, path: &[&str]) -> Option<Vec<String>> {
260        let child = tree.get_child(path)?;
261        let children = child.children.as_ref()?;
262        let mut children = children
263            .values()
264            .map(|child| child.value.to_string())
265            .collect::<Vec<_>>();
266        children.sort();
267        Some(children)
268    }
269
270    #[test]
271    fn test_values_are_returned_by_path() {
272        let tree = create_tree();
273        assert_eq!(get_values(&tree, &[]), str_vec![
274            "Alpha",
275            "Bravo",
276        ]);
277        assert_eq!(get_values(&tree, &["bravo"]), str_vec![
278            "Echo",
279            "Foxtrot",
280            "Golf",
281            "Hotel",
282        ]);
283        assert_eq!(get_values(&tree, &["", "alpha", "", "charlie", ""]), str_vec![
284            "India",
285            "Juliet",
286        ]);
287        assert_eq!(get_values(&tree, &["alpha", "delta", "lima"]), str_vec![
288            "Yankee",
289            "Zulu",
290        ]);
291        assert_eq!(get_values(&tree, &["unknown"]).is_empty(), true);
292        assert_eq!(get_values(&tree, &["unknown", "unknown"]).is_empty(), true);
293        assert_eq!(get_values(&tree, &["alpha", "unknown"]).is_empty(), true);
294    }
295
296    #[test]
297    fn test_nodes_are_returned_by_path() {
298        let tree = create_tree();
299        assert_eq!(get_nodes(&tree, &[]), str_vec![
300            "Alpha",
301            "Bravo",
302        ]);
303        assert_eq!(get_nodes(&tree, &["bravo"]), str_vec![
304            "Echo",
305            "Foxtrot",
306            "Golf",
307            "Hotel",
308        ]);
309        assert_eq!(get_nodes(&tree, &["", "alpha", "", "charlie", ""]), str_vec![
310            "India",
311            "Juliet",
312        ]);
313        assert_eq!(get_nodes(&tree, &["alpha", "delta", "lima"]), str_vec![
314            "Yankee",
315            "Zulu",
316        ]);
317        assert_eq!(get_nodes(&tree, &["unknown"]).is_empty(), true);
318        assert_eq!(get_nodes(&tree, &["unknown", "unknown"]).is_empty(), true);
319        assert_eq!(get_nodes(&tree, &["alpha", "unknown"]).is_empty(), true);
320    }
321
322    fn create_tree() -> CaseTree {
323        let mut tree = CaseTree::new(cow_str!(""));
324        tree.insert_child(cow_arr!["Alpha", "Charlie", "India"], cow_str!("Sierra"));
325        tree.insert_child(cow_arr!["Alpha", "Charlie", "India"], cow_str!("Tango"));
326        tree.insert_child(cow_arr!["Alpha", "Charlie", "Juliet"], cow_str!("Uniform"));
327        tree.insert_child(cow_arr!["Alpha", "Charlie", "Juliet"], cow_str!("Victor"));
328        tree.insert_child(cow_arr!["Alpha", "Delta", "Kilo"], cow_str!("Whiskey"));
329        tree.insert_child(cow_arr!["Alpha", "Delta", "Kilo"], cow_str!("Xray"));
330        tree.insert_child(cow_arr!["Alpha", "Delta", "Lima"], cow_str!("Yankee"));
331        tree.insert_child(cow_arr!["Alpha", "Delta", "Lima"], cow_str!("Zulu"));
332        tree.insert_child(cow_arr!["Bravo", "Echo"], cow_str!("Mike"));
333        tree.insert_child(cow_arr!["Bravo", "Echo"], cow_str!("November"));
334        tree.insert_child(cow_arr!["Bravo", "Echo"], cow_str!("Oscar"));
335        tree.insert_child(cow_arr!["Bravo", "Foxtrot"], cow_str!("Papa"));
336        tree.insert_child(cow_arr!["Bravo", "Foxtrot"], cow_str!("Quebec"));
337        tree.insert_child(cow_arr!["Bravo", "Golf"], cow_str!("Romeo"));
338        tree.insert_child(cow_arr!["Bravo"], cow_str!("Hotel"));
339        tree
340    }
341
342    fn get_values<'a>(tree: &'a CaseTree, path: &[&str]) -> Vec<&'a str> {
343        let values = tree
344            .get_values(path)
345            .map(|(key, (value, _))| (key, value.as_ref()))
346            .collect::<BTreeMap<_, _>>();
347        values.into_values().collect()
348    }
349
350    fn get_nodes<'a>(tree: &'a CaseTree, path: &[&str]) -> Vec<&'a str> {
351        if let Some(child) = tree.get_child(path) {
352            let values = child
353                .get_nodes(Entity::Default)
354                .map(|(key, child)| (key, child.as_str()))
355                .collect::<BTreeMap<_, _>>();
356            values.into_values().collect()
357        } else {
358            Vec::new()
359        }
360    }
361}