database_tree/
databasetree.rs

1use crate::{
2    databasetreeitems::DatabaseTreeItems, error::Result, item::DatabaseTreeItemKind,
3    tree_iter::TreeIterator,
4};
5use crate::{Database, Table};
6use std::{collections::BTreeSet, usize};
7
8///
9#[derive(Copy, Clone, Debug)]
10pub enum MoveSelection {
11    Up,
12    Down,
13    MultipleUp,
14    MultipleDown,
15    Left,
16    Right,
17    Top,
18    End,
19    Enter,
20}
21
22#[derive(Debug, Clone, Copy)]
23pub struct VisualSelection {
24    pub count: usize,
25    pub index: usize,
26}
27
28/// wraps `DatabaseTreeItems` as a datastore and adds selection functionality
29#[derive(Default)]
30pub struct DatabaseTree {
31    items: DatabaseTreeItems,
32    pub selection: Option<usize>,
33    visual_selection: Option<VisualSelection>,
34}
35
36impl DatabaseTree {
37    pub fn new(list: &[crate::Database], collapsed: &BTreeSet<&String>) -> Result<Self> {
38        let mut new_self = Self {
39            items: DatabaseTreeItems::new(list, collapsed)?,
40            selection: if list.is_empty() { None } else { Some(0) },
41            visual_selection: None,
42        };
43        new_self.visual_selection = new_self.calc_visual_selection();
44
45        Ok(new_self)
46    }
47
48    pub fn filter(&self, filter_text: String) -> Self {
49        let mut new_self = Self {
50            items: self.items.filter(filter_text),
51            selection: Some(0),
52            visual_selection: None,
53        };
54        new_self.visual_selection = new_self.calc_visual_selection();
55        new_self
56    }
57
58    pub fn collapse_but_root(&mut self) {
59        self.items.collapse(0, true);
60        self.items.expand(0, false);
61    }
62
63    /// iterates visible elements starting from `start_index_visual`
64    pub fn iterate(&self, start_index_visual: usize, max_amount: usize) -> TreeIterator<'_> {
65        let start = self
66            .visual_index_to_absolute(start_index_visual)
67            .unwrap_or_default();
68        TreeIterator::new(self.items.iterate(start, max_amount), self.selection)
69    }
70
71    pub const fn visual_selection(&self) -> Option<&VisualSelection> {
72        self.visual_selection.as_ref()
73    }
74
75    pub fn selected_item(&self) -> Option<&crate::DatabaseTreeItem> {
76        self.selection
77            .and_then(|index| self.items.tree_items.get(index))
78    }
79
80    pub fn selected_table(&self) -> Option<(Database, Table)> {
81        self.selection.and_then(|index| {
82            let item = &self.items.tree_items[index];
83            match item.kind() {
84                DatabaseTreeItemKind::Database { .. } => None,
85                DatabaseTreeItemKind::Table { table, database } => {
86                    Some((database.clone(), table.clone()))
87                }
88                DatabaseTreeItemKind::Schema { .. } => None,
89            }
90        })
91    }
92
93    pub fn collapse_recursive(&mut self) {
94        if let Some(selection) = self.selection {
95            self.items.collapse(selection, true);
96        }
97    }
98
99    pub fn expand_recursive(&mut self) {
100        if let Some(selection) = self.selection {
101            self.items.expand(selection, true);
102        }
103    }
104
105    pub fn move_selection(&mut self, dir: MoveSelection) -> bool {
106        self.selection.map_or(false, |selection| {
107            let new_index = match dir {
108                MoveSelection::Up => self.selection_up(selection, 1),
109                MoveSelection::Down => self.selection_down(selection, 1),
110                MoveSelection::MultipleUp => self.selection_up(selection, 10),
111                MoveSelection::MultipleDown => self.selection_down(selection, 10),
112                MoveSelection::Left => self.selection_left(selection),
113                MoveSelection::Right => self.selection_right(selection),
114                MoveSelection::Top => Self::selection_start(selection),
115                MoveSelection::End => self.selection_end(selection),
116                MoveSelection::Enter => self.expand(selection),
117            };
118
119            let changed_index = new_index.map(|i| i != selection).unwrap_or_default();
120
121            if changed_index {
122                self.selection = new_index;
123                self.visual_selection = self.calc_visual_selection();
124            }
125
126            changed_index || new_index.is_some()
127        })
128    }
129
130    fn visual_index_to_absolute(&self, visual_index: usize) -> Option<usize> {
131        self.items
132            .iterate(0, self.items.len())
133            .enumerate()
134            .find_map(
135                |(i, (abs, _))| {
136                    if i == visual_index {
137                        Some(abs)
138                    } else {
139                        None
140                    }
141                },
142            )
143    }
144
145    fn calc_visual_selection(&self) -> Option<VisualSelection> {
146        self.selection.map(|selection_absolute| {
147            let mut count = 0;
148            let mut visual_index = 0;
149            for (index, _item) in self.items.iterate(0, self.items.len()) {
150                if selection_absolute == index {
151                    visual_index = count;
152                }
153
154                count += 1;
155            }
156
157            VisualSelection {
158                index: visual_index,
159                count,
160            }
161        })
162    }
163
164    const fn selection_start(current_index: usize) -> Option<usize> {
165        if current_index == 0 {
166            None
167        } else {
168            Some(0)
169        }
170    }
171
172    fn selection_end(&self, current_index: usize) -> Option<usize> {
173        let items_max = self.items.len().saturating_sub(1);
174
175        let mut new_index = items_max;
176
177        loop {
178            if self.is_visible_index(new_index) {
179                break;
180            }
181
182            if new_index == 0 {
183                break;
184            }
185
186            new_index = new_index.saturating_sub(1);
187            new_index = std::cmp::min(new_index, items_max);
188        }
189
190        if new_index == current_index {
191            None
192        } else {
193            Some(new_index)
194        }
195    }
196
197    fn selection_up(&self, current_index: usize, lines: usize) -> Option<usize> {
198        let mut index = current_index;
199
200        'a: for _ in 0..lines {
201            loop {
202                if index == 0 {
203                    break 'a;
204                }
205
206                index = index.saturating_sub(1);
207
208                if self.is_visible_index(index) {
209                    break;
210                }
211            }
212        }
213
214        if index == current_index {
215            None
216        } else {
217            Some(index)
218        }
219    }
220
221    fn selection_down(&self, current_index: usize, lines: usize) -> Option<usize> {
222        let mut index = current_index;
223        let last_visible_item_index = self
224            .items
225            .tree_items
226            .iter()
227            .rposition(|x| x.info().is_visible())?;
228
229        'a: for _ in 0..lines {
230            loop {
231                if index >= last_visible_item_index {
232                    break 'a;
233                }
234
235                index = index.saturating_add(1);
236
237                if self.is_visible_index(index) {
238                    break;
239                }
240            }
241        }
242
243        if index == current_index {
244            None
245        } else {
246            Some(index)
247        }
248    }
249
250    fn selection_updown(&self, current_index: usize, up: bool) -> Option<usize> {
251        let mut index = current_index;
252
253        loop {
254            index = {
255                let new_index = if up {
256                    index.saturating_sub(1)
257                } else {
258                    index.saturating_add(1)
259                };
260
261                if new_index == index {
262                    break;
263                }
264
265                if new_index >= self.items.len() {
266                    break;
267                }
268
269                new_index
270            };
271
272            if self.is_visible_index(index) {
273                break;
274            }
275        }
276
277        if index == current_index {
278            None
279        } else {
280            Some(index)
281        }
282    }
283
284    fn select_parent(&mut self, current_index: usize) -> Option<usize> {
285        let indent = self.items.tree_items.get(current_index)?.info().indent();
286
287        let mut index = current_index;
288
289        while let Some(selection) = self.selection_updown(index, true) {
290            index = selection;
291
292            if self.items.tree_items[index].info().indent() < indent {
293                break;
294            }
295        }
296
297        if index == current_index {
298            None
299        } else {
300            Some(index)
301        }
302    }
303
304    fn selection_left(&mut self, current_index: usize) -> Option<usize> {
305        let item = &mut self.items.tree_items.get(current_index)?;
306
307        if item.kind().is_database() && !item.kind().is_database_collapsed() {
308            self.items.collapse(current_index, false);
309            return Some(current_index);
310        }
311
312        if item.kind().is_schema() && !item.kind().is_schema_collapsed() {
313            self.items.collapse(current_index, false);
314            return Some(current_index);
315        }
316
317        self.select_parent(current_index)
318    }
319
320    fn expand(&mut self, current_selection: usize) -> Option<usize> {
321        let item = &mut self.items.tree_items.get(current_selection)?;
322
323        if item.kind().is_database() && item.kind().is_database_collapsed() {
324            self.items.expand(current_selection, false);
325            return Some(current_selection);
326        }
327
328        if item.kind().is_schema() && item.kind().is_schema_collapsed() {
329            self.items.expand(current_selection, false);
330            return Some(current_selection);
331        }
332
333        None
334    }
335
336    fn selection_right(&mut self, current_selection: usize) -> Option<usize> {
337        let item = &mut self.items.tree_items.get(current_selection)?;
338
339        if item.kind().is_database() {
340            if item.kind().is_database_collapsed() {
341                self.items.expand(current_selection, false);
342                return Some(current_selection);
343            }
344            return self.selection_updown(current_selection, false);
345        }
346
347        if item.kind().is_schema() {
348            if item.kind().is_schema_collapsed() {
349                self.items.expand(current_selection, false);
350                return Some(current_selection);
351            }
352            return self.selection_updown(current_selection, false);
353        }
354
355        None
356    }
357
358    fn is_visible_index(&self, index: usize) -> bool {
359        self.items
360            .tree_items
361            .get(index)
362            .map(|item| item.info().is_visible())
363            .unwrap_or_default()
364    }
365}
366
367#[cfg(test)]
368mod test {
369    use crate::{Database, DatabaseTree, MoveSelection, Schema, Table};
370    use std::collections::BTreeSet;
371
372    impl Table {
373        fn new(name: String) -> Self {
374            Table {
375                name,
376                create_time: None,
377                update_time: None,
378                engine: None,
379                schema: None,
380            }
381        }
382
383        fn new_with_schema(name: String, schema: String) -> Self {
384            Table {
385                name,
386                create_time: None,
387                update_time: None,
388                engine: None,
389                schema: Some(schema),
390            }
391        }
392    }
393
394    #[test]
395    fn test_selection() {
396        let items = vec![Database::new(
397            "a".to_string(),
398            vec![Table::new("b".to_string()).into()],
399        )];
400
401        // a
402        //   b
403
404        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
405
406        assert!(tree.move_selection(MoveSelection::Right));
407        assert_eq!(tree.selection, Some(0));
408        assert!(tree.move_selection(MoveSelection::Down));
409        assert_eq!(tree.selection, Some(1));
410
411        let items = vec![Database::new(
412            "a".to_string(),
413            vec![Schema {
414                name: "b".to_string(),
415                tables: vec![Table::new("c".to_string()).into()],
416            }
417            .into()],
418        )];
419
420        // a
421        //   b
422        //     c
423
424        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
425
426        assert!(tree.move_selection(MoveSelection::Right));
427        assert_eq!(tree.selection, Some(0));
428        assert!(tree.move_selection(MoveSelection::Down));
429        assert_eq!(tree.selection, Some(1));
430        assert!(tree.move_selection(MoveSelection::Right));
431        assert_eq!(tree.selection, Some(1));
432        assert!(tree.move_selection(MoveSelection::Down));
433        assert_eq!(tree.selection, Some(2));
434    }
435
436    #[test]
437    fn test_expand() {
438        let items = vec![Database::new(
439            "a".to_string(),
440            vec![Table::new("b".to_string()).into()],
441        )];
442
443        // a
444        //   b
445
446        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
447
448        assert!(tree.move_selection(MoveSelection::Enter));
449        assert!(!tree.items.tree_items[0].kind().is_database_collapsed());
450        assert_eq!(tree.selection, Some(0));
451
452        assert!(tree.move_selection(MoveSelection::Down));
453        assert_eq!(tree.selection, Some(1));
454
455        assert!(!tree.move_selection(MoveSelection::Enter));
456        assert_eq!(tree.selection, Some(1));
457
458        let items = vec![Database::new(
459            "a".to_string(),
460            vec![Schema {
461                name: "b".to_string(),
462                tables: vec![Table::new("c".to_string()).into()],
463            }
464            .into()],
465        )];
466
467        // a
468        //   b
469        //     c
470
471        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
472
473        assert!(tree.move_selection(MoveSelection::Enter));
474        assert!(!tree.items.tree_items[0].kind().is_database_collapsed());
475        assert!(tree.items.tree_items[1].kind().is_schema_collapsed());
476        assert_eq!(tree.selection, Some(0));
477
478        assert!(tree.move_selection(MoveSelection::Down));
479        assert_eq!(tree.selection, Some(1));
480
481        assert!(tree.move_selection(MoveSelection::Enter));
482        assert!(!tree.items.tree_items[1].kind().is_database_collapsed());
483        assert_eq!(tree.selection, Some(1));
484
485        assert!(tree.move_selection(MoveSelection::Down));
486        assert_eq!(tree.selection, Some(2));
487
488        assert!(!tree.move_selection(MoveSelection::Enter));
489        assert_eq!(tree.selection, Some(2));
490    }
491
492    #[test]
493    fn test_selection_multiple_up_down() {
494        let items = vec![Database::new(
495            "a".to_string(),
496            vec!["b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]
497                .iter()
498                .map(|x| Table::new(x.to_string()).into())
499                .collect(),
500        )];
501
502        // a
503        //   b
504        //   ...
505        //   j
506
507        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
508
509        assert!(tree.move_selection(MoveSelection::Right));
510        assert_eq!(tree.selection, Some(0));
511        assert!(tree.move_selection(MoveSelection::MultipleDown));
512        assert_eq!(tree.selection, Some(10));
513
514        tree.selection = Some(11);
515        assert!(tree.move_selection(MoveSelection::MultipleUp));
516        assert_eq!(tree.selection, Some(1));
517
518        let items = vec![Database::new(
519            "a".to_string(),
520            vec![Schema {
521                name: "b".to_string(),
522                tables: vec!["c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]
523                    .iter()
524                    .map(|x| Table::new(x.to_string()).into())
525                    .collect(),
526            }
527            .into()],
528        )];
529
530        // a
531        //   b
532        //     c
533        //     ...
534        //     l
535
536        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
537
538        assert!(tree.move_selection(MoveSelection::Right));
539        assert_eq!(tree.selection, Some(0));
540        assert!(tree.move_selection(MoveSelection::MultipleDown));
541        assert_eq!(tree.selection, Some(10));
542
543        tree.selection = Some(11);
544        assert!(tree.move_selection(MoveSelection::MultipleUp));
545        assert_eq!(tree.selection, Some(1));
546    }
547
548    #[test]
549    fn test_selection_skips_collapsed() {
550        let items = vec![
551            Database::new(
552                "a".to_string(),
553                vec![
554                    Table::new("b".to_string()).into(),
555                    Table::new("c".to_string()).into(),
556                ],
557            ),
558            Database::new("d".to_string(), vec![Table::new("e".to_string()).into()]),
559        ];
560
561        // a
562        //   b
563        //   c
564        // d
565        //   e
566
567        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
568        tree.selection = Some(1);
569
570        assert!(tree.move_selection(MoveSelection::Down));
571        assert_eq!(tree.selection, Some(3));
572
573        let items = vec![
574            Database::new(
575                "a".to_string(),
576                vec![Schema {
577                    name: "b".to_string(),
578                    tables: vec![Table::new("c".to_string()).into()],
579                }
580                .into()],
581            ),
582            Database::new(
583                "d".to_string(),
584                vec![Schema {
585                    name: "e".to_string(),
586                    tables: vec![Table::new("f".to_string()).into()],
587                }
588                .into()],
589            ),
590        ];
591
592        // a
593        //   b
594        //     c
595        // d
596        //   e
597        //     f
598
599        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
600        tree.selection = Some(1);
601
602        assert!(tree.move_selection(MoveSelection::Down));
603        assert_eq!(tree.selection, Some(3));
604    }
605
606    #[test]
607    fn test_selection_left_collapse() {
608        let items = vec![Database::new(
609            "a".to_string(),
610            vec![
611                Table::new("b".to_string()).into(),
612                Table::new("c".to_string()).into(),
613            ],
614        )];
615
616        // a
617        //   b
618        //   c
619
620        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
621        tree.selection = Some(0);
622        tree.items.expand(0, false);
623
624        assert!(tree.move_selection(MoveSelection::Left));
625        assert_eq!(tree.selection, Some(0));
626        assert!(tree.items.tree_items[0].kind().is_database_collapsed());
627        assert!(!tree.items.tree_items[1].info().is_visible());
628        assert!(!tree.items.tree_items[2].info().is_visible());
629
630        let items = vec![
631            Database::new(
632                "a".to_string(),
633                vec![Schema {
634                    name: "b".to_string(),
635                    tables: vec![Table::new("c".to_string()).into()],
636                }
637                .into()],
638            ),
639            Database::new(
640                "d".to_string(),
641                vec![Schema {
642                    name: "e".to_string(),
643                    tables: vec![Table::new("f".to_string()).into()],
644                }
645                .into()],
646            ),
647        ];
648
649        // a
650        //   b
651        //     c
652        // d
653        //   e
654        //     f
655
656        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
657        tree.selection = Some(0);
658        tree.items.expand(0, false);
659
660        assert!(tree.move_selection(MoveSelection::Left));
661        assert_eq!(tree.selection, Some(0));
662        assert!(tree.items.tree_items[0].kind().is_database_collapsed());
663        assert!(!tree.items.tree_items[1].info().is_visible());
664        assert!(!tree.items.tree_items[2].info().is_visible());
665    }
666
667    #[test]
668    fn test_selection_left_parent() {
669        let items = vec![Database::new(
670            "a".to_string(),
671            vec![
672                Table::new("b".to_string()).into(),
673                Table::new("c".to_string()).into(),
674            ],
675        )];
676
677        // a
678        //   b
679        //   c
680
681        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
682        tree.selection = Some(2);
683        tree.items.expand(0, false);
684
685        assert!(tree.move_selection(MoveSelection::Left));
686        assert_eq!(tree.selection, Some(0));
687
688        let items = vec![Database::new(
689            "a".to_string(),
690            vec![Schema {
691                name: "b".to_string(),
692                tables: vec![Table::new_with_schema("c".to_string(), "a".to_string()).into()],
693            }
694            .into()],
695        )];
696
697        // a
698        //   b
699        //     c
700
701        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
702
703        tree.selection = Some(2);
704        tree.items.expand(0, false);
705        tree.items.expand(1, false);
706        assert!(tree.move_selection(MoveSelection::Left));
707        assert_eq!(tree.selection, Some(1));
708
709        assert!(tree.move_selection(MoveSelection::Left));
710        assert_eq!(tree.selection, Some(1));
711
712        assert!(tree.move_selection(MoveSelection::Left));
713        assert_eq!(tree.selection, Some(0));
714    }
715
716    #[test]
717    fn test_selection_right_expand() {
718        let items = vec![Database::new(
719            "a".to_string(),
720            vec![
721                Table::new("b".to_string()).into(),
722                Table::new("c".to_string()).into(),
723            ],
724        )];
725
726        // a
727        //   b
728        //   c
729
730        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
731        tree.selection = Some(0);
732
733        assert!(tree.move_selection(MoveSelection::Right));
734        assert_eq!(tree.selection, Some(0));
735        assert!(!tree.items.tree_items[0].kind().is_database_collapsed());
736
737        assert!(tree.move_selection(MoveSelection::Right));
738        assert_eq!(tree.selection, Some(1));
739
740        let items = vec![Database::new(
741            "a".to_string(),
742            vec![Schema {
743                name: "b".to_string(),
744                tables: vec![Table::new_with_schema("c".to_string(), "a".to_string()).into()],
745            }
746            .into()],
747        )];
748
749        // a
750        //   b
751        //     c
752
753        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
754        tree.selection = Some(0);
755
756        assert!(tree.move_selection(MoveSelection::Right));
757        assert_eq!(tree.selection, Some(0));
758        assert!(!tree.items.tree_items[0].kind().is_database_collapsed());
759
760        assert!(tree.move_selection(MoveSelection::Right));
761        assert_eq!(tree.selection, Some(1));
762
763        assert!(tree.move_selection(MoveSelection::Right));
764        assert_eq!(tree.selection, Some(1));
765        assert!(!tree.items.tree_items[0].kind().is_schema_collapsed());
766    }
767
768    #[test]
769    fn test_visible_selection() {
770        let items = vec![
771            Database::new(
772                "a".to_string(),
773                vec![
774                    Table::new("b".to_string()).into(),
775                    Table::new("c".to_string()).into(),
776                ],
777            ),
778            Database::new("d".to_string(), vec![Table::new("e".to_string()).into()]),
779        ];
780
781        // a
782        //   b
783        //   c
784        // d
785        //   e
786
787        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
788        tree.items.expand(0, false);
789        tree.items.expand(3, false);
790
791        tree.selection = Some(0);
792        assert!(tree.move_selection(MoveSelection::Left));
793        assert!(tree.move_selection(MoveSelection::Down));
794        assert_eq!(tree.selection, Some(3));
795        let s = tree.visual_selection().unwrap();
796
797        assert_eq!(s.count, 3);
798        assert_eq!(s.index, 1);
799
800        let items = vec![
801            Database::new(
802                "a".to_string(),
803                vec![Schema {
804                    name: "b".to_string(),
805                    tables: vec![Table::new_with_schema("c".to_string(), "a".to_string()).into()],
806                }
807                .into()],
808            ),
809            Database::new(
810                "d".to_string(),
811                vec![Schema {
812                    name: "e".to_string(),
813                    tables: vec![Table::new_with_schema("f".to_string(), "d".to_string()).into()],
814                }
815                .into()],
816            ),
817        ];
818
819        // a
820        //   b
821        //     c
822        // d
823        //   e
824        //     f
825
826        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
827        tree.items.expand(0, false);
828        tree.items.expand(1, false);
829        tree.items.expand(3, false);
830        tree.selection = Some(0);
831
832        assert!(tree.move_selection(MoveSelection::Left));
833        assert!(tree.move_selection(MoveSelection::Down));
834        assert_eq!(tree.selection, Some(3));
835        let s = tree.visual_selection().unwrap();
836
837        assert_eq!(s.count, 4);
838        assert_eq!(s.index, 1);
839    }
840
841    #[test]
842    fn test_selection_top() {
843        let items = vec![Database::new(
844            "a".to_string(),
845            vec![
846                Table::new("b".to_string()).into(),
847                Table::new("c".to_string()).into(),
848                Table::new("d".to_string()).into(),
849            ],
850        )];
851
852        // a
853        //   b
854        //   c
855        //   d
856
857        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
858        tree.selection = Some(3);
859        tree.items.expand(0, false);
860
861        assert!(tree.move_selection(MoveSelection::Top));
862        assert_eq!(tree.selection, Some(0));
863
864        let items = vec![Database::new(
865            "a".to_string(),
866            vec![Schema {
867                name: "b".to_string(),
868                tables: vec![
869                    Table::new("c".to_string()).into(),
870                    Table::new("d".to_string()).into(),
871                ],
872            }
873            .into()],
874        )];
875
876        // a
877        //   b
878        //     c
879        //     d
880
881        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
882        tree.selection = Some(3);
883        tree.items.expand(0, false);
884        tree.items.expand(1, false);
885
886        assert!(tree.move_selection(MoveSelection::Top));
887        assert_eq!(tree.selection, Some(0));
888    }
889
890    #[test]
891    fn test_selection_bottom() {
892        let items = vec![Database::new(
893            "a".to_string(),
894            vec![
895                Table::new("b".to_string()).into(),
896                Table::new("c".to_string()).into(),
897                Table::new("d".to_string()).into(),
898            ],
899        )];
900
901        // a
902        //   b
903        //   c
904        //   d
905
906        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
907        tree.selection = Some(0);
908        tree.items.expand(0, false);
909
910        assert!(tree.move_selection(MoveSelection::End));
911        assert_eq!(tree.selection, Some(3));
912
913        let items = vec![Database::new(
914            "a".to_string(),
915            vec![Schema {
916                name: "b".to_string(),
917                tables: vec![
918                    Table::new("c".to_string()).into(),
919                    Table::new("d".to_string()).into(),
920                ],
921            }
922            .into()],
923        )];
924
925        // a
926        //   b
927        //     c
928        //     d
929
930        let mut tree = DatabaseTree::new(&items, &BTreeSet::new()).unwrap();
931        tree.selection = Some(0);
932        tree.items.expand(0, false);
933        tree.items.expand(1, false);
934
935        assert!(tree.move_selection(MoveSelection::End));
936        assert_eq!(tree.selection, Some(3));
937    }
938}