Skip to main content

the_zipper/
lib.rs

1//! # The Zipper
2//!
3//! The Zipper is a Rust library designed to provide efficient and ergonomic utilities
4//! for working with data structures using the zipper pattern. This library simplifies
5//! navigation and modification of complex data structures while maintaining immutability.
6//!
7//! HUET G. The Zipper. *Journal of Functional Programming*. 1997;7(5):549–554.
8//! doi:[10.1017/S0956796897002864](https://doi.org/10.1017/S0956796897002864)
9//!
10//! ## Features
11//!
12//! - Easy-to-use API for zipper-based data manipulation.
13//! - Supports various data structures like trees and lists.
14//! - Lightweight and performant.
15//! - Code coverage is 100%.
16//!
17//! ## Installation
18//!
19//! Add the following to your `Cargo.toml`:
20//!
21//! ```toml
22//! [dependencies]
23//! the_zipper = "0.1.2"
24//! ```
25//!
26//! ## Usage
27//!
28//! ```rust
29//! use the_zipper::{Tree, Location, Path};
30//!
31//! fn main() {
32//!     let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
33//!
34//!     let location = Location::new(tree);
35//!
36//!     let location = location.go_down().unwrap();
37//!     assert_eq!(location.cursor, Tree::Item("a"));
38//!
39//!     let location = location.go_right().unwrap();
40//!     assert_eq!(location.cursor, Tree::Item("+"));
41//!
42//!     let location = location.go_left().unwrap();
43//!     assert_eq!(location.cursor, Tree::Item("a"));
44//!
45//!     let location = location.insert_right(Tree::Item(".")).unwrap();
46//!     assert_eq!(
47//!         location,
48//!         Location {
49//!             cursor: Tree::Item("a"),
50//!             path: Path::Node {
51//!                 left: vec![],
52//!                 right: vec![Tree::Item("."), Tree::Item("+"), Tree::Item("b")],
53//!                 path: Path::Node {
54//!                     left: vec![],
55//!                     right: vec![Tree::Section(vec![
56//!                         Tree::Item("a"),
57//!                         Tree::Item("+"),
58//!                         Tree::Item("b")
59//!                     ])],
60//!                     path: Path::Top.into()
61//!                 }
62//!                 .into()
63//!             }
64//!             .into()
65//!         }
66//!         .into()
67//!     );
68//! }
69//! ```
70//!
71//! ## Links
72//!
73//! [F# Implementation](https://github.com/Denys-Bushulyak/the-zipper-fsharp)
74//!
75//! ## License
76//!
77//! This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
78
79use std::rc::Rc;
80
81#[derive(Debug, Clone, PartialEq)]
82/// Represents a hierarchical tree structure.
83///
84/// A tree can either be a single item or a section containing multiple trees.
85pub enum Tree<T: Clone> {
86    /// A single item value of type T.
87    Item(T),
88    /// A collection of trees forming a section.
89    Section(Vec<Tree<T>>),
90}
91
92#[derive(Debug, Clone, PartialEq)]
93/// Represents a path within a tree, used for navigation and context tracking.
94///
95/// The path keeps track of the location in the tree structure,
96/// remembering the siblings to the left and right of the current position,
97/// as well as the path to the parent.
98pub enum Path<T: Clone> {
99    /// Represents the top level of the tree hierarchy.
100    Top,
101    /// Represents a position within the tree structure.
102    Node {
103        /// Trees to the left of the current position.
104        left: Vec<Tree<T>>,
105        /// Trees to the right of the current position.
106        right: Vec<Tree<T>>,
107        /// Path to the parent node.
108        path: Rc<Path<T>>,
109    },
110}
111
112#[derive(Debug, PartialEq, Clone)]
113/// Represents a location (cursor) within a tree.
114///
115/// A location combines a cursor pointing to the current tree node
116/// and a path providing context for navigation within the overall tree structure.
117pub struct Location<T: Clone> {
118    /// The current tree node being focused on.
119    pub cursor: Tree<T>,
120    /// The path representing the context of this location within the overall tree.
121    pub path: Rc<Path<T>>,
122}
123
124impl<T: Clone> Location<T> {
125    /// Creates a new location from a tree.
126    ///
127    /// # Arguments
128    ///
129    /// * `tree` - The tree to create a location from.
130    ///
131    /// # Returns
132    ///
133    /// A new `Location` instance with the given tree as cursor.
134    pub fn new(tree: Tree<T>) -> Self {
135        Self {
136            cursor: tree.clone(),
137            path: Path::Node {
138                left: vec![],
139                right: vec![tree.clone()],
140                path: Rc::new(Path::Top),
141            }
142            .into(),
143        }
144    }
145
146    /// Moves the cursor to the left sibling.
147    ///
148    /// # Returns
149    ///
150    /// * `Some(Location)` - If there is a left sibling.
151    /// * `None` - If there is no left sibling or the location is at the top.
152    pub fn go_left(self) -> Option<Self> {
153        match self.path.as_ref() {
154            Path::Top => None,
155            Path::Node { left, right, path } => left.split_first().map(|(first, rest)| Self {
156                cursor: first.clone(),
157                path: Path::Node {
158                    left: rest.to_vec(),
159                    path: path.clone(),
160                    right: vec![self.cursor].into_iter().chain(right.clone()).collect(),
161                }
162                .into(),
163            }),
164        }
165    }
166
167    /// Moves the cursor to the right sibling.
168    ///
169    /// # Returns
170    ///
171    /// * `Some(Location)` - If there is a right sibling.
172    /// * `None` - If there is no right sibling or the location is at the top.
173    pub fn go_right(self) -> Option<Self> {
174        match self.path.as_ref() {
175            Path::Top => None,
176            Path::Node { left, right, path } => right.split_first().map(|(first, rest)| Self {
177                cursor: first.clone(),
178                path: Path::Node {
179                    left: vec![self.cursor].into_iter().chain(left.clone()).collect(),
180                    right: rest.to_vec(),
181                    path: path.clone(),
182                }
183                .into(),
184            }),
185        }
186    }
187
188    /// Moves the cursor to the parent node.
189    ///
190    /// # Returns
191    ///
192    /// * `Some(Location)` - If there is a parent node.
193    /// * `None` - If the location is at the top.
194    pub fn go_up(self) -> Option<Self> {
195        match self.path.as_ref() {
196            Path::Top => None,
197            Path::Node { left, right, path } => {
198                let left = left.iter().rev().cloned().collect::<Vec<Tree<T>>>();
199                Self {
200                    path: path.clone(),
201                    cursor: Tree::Section(
202                        [left, vec![self.cursor], right.clone()]
203                            .iter()
204                            .flatten()
205                            .cloned()
206                            .collect::<Vec<Tree<T>>>(),
207                    ),
208                }
209                .into()
210            }
211        }
212    }
213
214    /// Moves the cursor to the first child node.
215    ///
216    /// # Returns
217    ///
218    /// * `Some(Location)` - If the current node is a section with at least one child.
219    /// * `None` - If the current node is an item or an empty section.
220    pub fn go_down(self) -> Option<Self> {
221        match self.cursor {
222            Tree::Item(_) => None,
223            Tree::Section(trees) => trees.split_first().map(|(first, rest)| Self {
224                cursor: first.clone(),
225                path: Path::Node {
226                    left: vec![],
227                    right: rest.into(),
228                    path: self.path,
229                }
230                .into(),
231            }),
232        }
233    }
234
235    /// Gets the nth child of the current node.
236    ///
237    /// This is equivalent to n calls to `go_right()`.
238    ///
239    /// # Arguments
240    ///
241    /// * `n` - The index of the child to navigate to.
242    ///
243    /// # Returns
244    ///
245    /// * `Some(Location)` - If the child exists.
246    /// * `None` - If the child doesn't exist or the current node is an item.
247    pub fn get_nth(self, n: usize) -> Option<Self> {
248        match n {
249            0 => self.go_down(),
250            n => self.get_nth(n - 1).and_then(Location::go_right),
251        }
252    }
253
254    /// Replaces the current node with a new tree.
255    ///
256    /// # Arguments
257    ///
258    /// * `tree` - The new tree to replace the current node with.
259    ///
260    /// # Returns
261    ///
262    /// A new location with the updated cursor.
263    pub fn change(self, tree: Tree<T>) -> Self {
264        Self {
265            cursor: tree,
266            path: self.path,
267        }
268    }
269
270    /// Inserts a new tree to the right of the current node.
271    ///
272    /// # Arguments
273    ///
274    /// * `tree` - The tree to insert.
275    ///
276    /// # Returns
277    ///
278    /// * `Some(Location)` - If the insertion was successful.
279    /// * `None` - If the location is at the top.
280    pub fn insert_right(self, tree: Tree<T>) -> Option<Self> {
281        match self.path.as_ref() {
282            Path::Top => None,
283            Path::Node { left, right, path } => Self {
284                cursor: self.cursor.clone(),
285                path: Path::Node {
286                    left: left.clone(),
287                    path: path.clone(),
288                    right: vec![tree].into_iter().chain(right.clone()).collect(),
289                }
290                .into(),
291            }
292            .into(),
293        }
294    }
295
296    /// Inserts a new tree to the left of the current node.
297    ///
298    /// # Arguments
299    ///
300    /// * `tree` - The tree to insert.
301    ///
302    /// # Returns
303    ///
304    /// * `Some(Location)` - If the insertion was successful.
305    /// * `None` - If the location is at the top.
306    pub fn insert_left(self, tree: Tree<T>) -> Option<Self> {
307        match self.path.as_ref() {
308            Path::Top => None,
309            Path::Node { left, right, path } => Self {
310                cursor: self.cursor.clone(),
311                path: Path::Node {
312                    left: vec![tree].into_iter().chain(left.clone()).collect(),
313                    right: right.to_vec(),
314                    path: path.clone(),
315                }
316                .into(),
317            }
318            .into(),
319        }
320    }
321
322    /// Inserts a new tree as the first child of the current node.
323    ///
324    /// # Arguments
325    ///
326    /// * `tree` - The tree to insert.
327    ///
328    /// # Returns
329    ///
330    /// * `Some(Location)` - If the current node is a section.
331    /// * `None` - If the current node is an item.
332    pub fn insert_down(self, tree: Tree<T>) -> Option<Self> {
333        match self.cursor {
334            Tree::Item(_) => None,
335            Tree::Section(children) => Some(Self {
336                cursor: tree,
337                path: Path::Node {
338                    left: vec![],
339                    right: children,
340                    path: self.path,
341                }
342                .into(),
343            }),
344        }
345    }
346
347    /// Deletes the current node and moves the cursor to a sibling or parent.
348    ///
349    /// # Returns
350    ///
351    /// * `Some(Location)` - If the deletion was successful.
352    /// * `None` - If the location is at the top.
353    pub fn delete(self) -> Option<Self> {
354        match self.path.as_ref() {
355            Path::Top => None,
356            Path::Node { left, right, path } => {
357                let left = left.as_slice();
358                let right = right.as_slice();
359
360                let result = match (left, path, right) {
361                    // In the middle with existing left and right
362                    (left, path, [first_right, rest_right @ ..]) => Self {
363                        cursor: first_right.clone(),
364                        path: crate::Path::Node {
365                            left: left.to_vec(),
366                            right: rest_right.to_vec(),
367                            path: path.clone(),
368                        }
369                        .into(),
370                    },
371
372                    // With empty right
373                    ([first_left, rest_left @ ..], path, &[]) => Self {
374                        cursor: first_left.clone(),
375                        path: crate::Path::Node {
376                            left: rest_left.to_vec(),
377                            right: vec![],
378                            path: path.clone(),
379                        }
380                        .into(),
381                    },
382                    // With empty right and left
383                    ([], path, []) => Self {
384                        cursor: Tree::Section(vec![]),
385                        path: path.clone(),
386                    },
387                };
388
389                result.into()
390            }
391        }
392    }
393}
394
395#[cfg(test)]
396mod test {
397
398    use std::rc::Rc;
399
400    use crate::{Location, Path, Tree};
401
402    #[test]
403    fn test_new() {
404        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
405
406        let location = Location::new(tree.clone());
407
408        assert_eq!(
409            location,
410            Location {
411                cursor: tree.clone(),
412                path: Path::Node {
413                    left: vec![],
414                    right: vec![tree],
415                    path: Rc::new(Path::Top),
416                }
417                .into(),
418            }
419        );
420    }
421
422    #[test]
423    fn test_for_readme() {
424        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
425
426        let location = Location::new(tree);
427
428        let location = location.go_down().unwrap();
429        assert_eq!(location.cursor, Tree::Item("a"));
430
431        let location = location.go_right().unwrap();
432        assert_eq!(location.cursor, Tree::Item("+"));
433
434        let location = location.go_left().unwrap();
435        assert_eq!(location.cursor, Tree::Item("a"));
436
437        let location = location.insert_right(Tree::Item(".")).unwrap();
438        assert_eq!(
439            location,
440            Location {
441                cursor: Tree::Item("a"),
442                path: Path::Node {
443                    left: vec![],
444                    right: vec![Tree::Item("."), Tree::Item("+"), Tree::Item("b")],
445                    path: Path::Node {
446                        left: vec![],
447                        right: vec![Tree::Section(vec![
448                            Tree::Item("a"),
449                            Tree::Item("+"),
450                            Tree::Item("b")
451                        ])],
452                        path: Path::Top.into()
453                    }
454                    .into()
455                }
456                .into()
457            }
458            .into()
459        );
460    }
461
462    #[test]
463    fn test_go_left_none() {
464        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
465
466        let location = Location {
467            path: Path::Top.into(),
468            cursor: tree,
469        };
470
471        assert_eq!(location.clone().go_left(), None);
472    }
473
474    #[test]
475    fn test_go_left() {
476        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
477
478        let result = Location {
479            path: Path::Node {
480                left: vec![Tree::Item("a")],
481                right: vec![Tree::Item("b")],
482                path: Path::Node {
483                    left: vec![],
484                    right: vec![tree.clone()],
485                    path: Path::Top.into(),
486                }
487                .into(),
488            }
489            .into(),
490            cursor: Tree::Item("+"),
491        }
492        .go_left();
493
494        let expect = Some(Location {
495            path: Path::Node {
496                left: vec![],
497                right: vec![Tree::Item("+"), Tree::Item("b")],
498                path: Path::Node {
499                    left: vec![],
500                    right: vec![tree],
501                    path: Path::Top.into(),
502                }
503                .into(),
504            }
505            .into(),
506            cursor: Tree::Item("a"),
507        });
508
509        assert_eq!(result, expect,);
510    }
511
512    #[test]
513    fn test_go_right() {
514        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
515
516        let result = Location {
517            path: Path::Node {
518                left: vec![Tree::Item("a")],
519                right: vec![Tree::Item("b")],
520                path: Path::Node {
521                    left: vec![],
522                    right: vec![tree.clone()],
523                    path: Path::Top.into(),
524                }
525                .into(),
526            }
527            .into(),
528            cursor: Tree::Item("+"),
529        }
530        .go_right();
531
532        let expect = Some(Location {
533            path: Path::Node {
534                right: vec![],
535                left: vec![Tree::Item("+"), Tree::Item("a")],
536                path: Path::Node {
537                    left: vec![],
538                    right: vec![tree],
539                    path: Path::Top.into(),
540                }
541                .into(),
542            }
543            .into(),
544            cursor: Tree::Item("b"),
545        });
546
547        assert_eq!(result, expect);
548    }
549
550    #[test]
551    fn test_go_right_none() {
552        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
553
554        let location = Location {
555            path: Path::Top.into(),
556            cursor: tree,
557        };
558
559        assert_eq!(location.clone().go_right(), None);
560    }
561
562    #[test]
563    fn test_go_up_none() {
564        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
565
566        let location = Location {
567            path: Path::Top.into(),
568            cursor: tree,
569        };
570
571        assert_eq!(location.clone().go_up(), None);
572    }
573
574    #[test]
575    fn test_go_up() {
576        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
577
578        let location = Location {
579            path: Path::Node {
580                left: vec![],
581                right: vec![Tree::Item("+"), Tree::Item("b")],
582                path: Path::Top.into(),
583            }
584            .into(),
585            cursor: Tree::Item("a"),
586        }
587        .go_up();
588
589        assert_eq!(
590            location,
591            Some(Location {
592                cursor: tree.clone(),
593                path: Path::Top.into(),
594            })
595        );
596    }
597
598    #[test]
599    fn test_go_down_none() {
600        let tree = Tree::Item("a");
601
602        let location = Location {
603            path: Path::Top.into(),
604            cursor: tree,
605        };
606
607        assert_eq!(location.go_down(), None);
608    }
609
610    #[test]
611    fn test_go_down() {
612        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
613
614        let location = Location {
615            path: Path::Top.into(),
616            cursor: tree,
617        };
618
619        assert_eq!(
620            location.go_down(),
621            Some(Location {
622                cursor: Tree::Item("a"),
623                path: Path::Node {
624                    left: [].into(),
625                    right: [Tree::Item("+"), Tree::Item("b")].into(),
626                    path: crate::Path::Top.into(),
627                }
628                .into()
629            })
630        );
631    }
632
633    #[test]
634    fn test_get_nth_0() {
635        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
636
637        let location = Location {
638            path: Path::Top.into(),
639            cursor: tree,
640        };
641
642        assert_eq!(
643            location.get_nth(0),
644            Some(Location {
645                cursor: Tree::Item("a"),
646                path: Path::Node {
647                    left: [].into(),
648                    right: [Tree::Item("+"), Tree::Item("b")].into(),
649                    path: crate::Path::Top.into(),
650                }
651                .into()
652            })
653        );
654    }
655
656    #[test]
657    fn test_get_nth_1() {
658        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
659
660        let location = Location {
661            path: Path::Top.into(),
662            cursor: tree,
663        };
664
665        assert_eq!(
666            location.get_nth(1),
667            Some(Location {
668                cursor: Tree::Item("+"),
669                path: Path::Node {
670                    left: [Tree::Item("a")].into(),
671                    right: [Tree::Item("b")].into(),
672                    path: crate::Path::Top.into(),
673                }
674                .into()
675            })
676        );
677    }
678
679    #[test]
680    fn test_get_nth_2() {
681        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
682
683        let location = Location {
684            path: Path::Top.into(),
685            cursor: tree,
686        };
687
688        assert_eq!(
689            location.get_nth(2),
690            Some(Location {
691                cursor: Tree::Item("b"),
692                path: Path::Node {
693                    left: [Tree::Item("+"), Tree::Item("a")].into(),
694                    right: [].into(),
695                    path: crate::Path::Top.into(),
696                }
697                .into()
698            })
699        );
700    }
701
702    #[test]
703    fn test_get_nth_out_of_bounds() {
704        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
705
706        let location = Location {
707            path: Path::Top.into(),
708            cursor: tree,
709        };
710
711        assert_eq!(location.get_nth(3), None);
712    }
713
714    #[test]
715    fn test_change() {
716        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
717
718        let location = Location {
719            path: Path::Top.into(),
720            cursor: tree,
721        };
722
723        let new_tree = Tree::Item("z");
724
725        assert_eq!(
726            location.change(new_tree.clone()),
727            Location {
728                cursor: new_tree,
729                path: Path::Top.into(),
730            }
731        );
732    }
733
734    #[test]
735    fn test_change_after_go_left() {
736        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
737
738        let location = Location {
739            path: Path::Top.into(),
740            cursor: tree,
741        };
742
743        let new_tree = Tree::Item("-");
744
745        let updated_location = location
746            .go_down()
747            .and_then(Location::go_right)
748            .map(|loc| loc.change(new_tree.clone()));
749
750        assert_eq!(
751            updated_location,
752            Some(Location {
753                cursor: Tree::Item("-"),
754                path: Path::Node {
755                    left: [Tree::Item("a")].into(),
756                    right: [Tree::Item("b")].into(),
757                    path: crate::Path::Top.into(),
758                }
759                .into()
760            })
761        );
762    }
763
764    #[test]
765    fn test_insert_left() {
766        let result = Location {
767            path: Path::Node {
768                left: vec![],
769                right: vec![Tree::Item("+"), Tree::Item("b")],
770                path: Path::Top.into(),
771            }
772            .into(),
773            cursor: Tree::Item("a"),
774        }
775        .insert_left(Tree::Item("."));
776
777        let expect = Location {
778            path: Path::Node {
779                left: vec![Tree::Item(".")],
780                right: vec![Tree::Item("+"), Tree::Item("b")],
781                path: Path::Top.into(),
782            }
783            .into(),
784            cursor: Tree::Item("a"),
785        }
786        .into();
787
788        assert_eq!(result, expect);
789    }
790
791    #[test]
792    fn test_insert_left_none() {
793        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
794
795        let location = Location {
796            path: Path::Top.into(),
797            cursor: tree,
798        };
799
800        let new_tree = Tree::Item("-");
801
802        assert!(location.insert_left(new_tree).is_none());
803    }
804
805    #[test]
806    fn test_insert_right_none() {
807        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
808
809        let location = Location {
810            path: Path::Top.into(),
811            cursor: tree,
812        };
813
814        let new_tree = Tree::Item("-");
815
816        assert!(location.insert_right(new_tree).is_none());
817    }
818
819    #[test]
820    fn test_insert_right() {
821        let result = Location {
822            path: Path::Node {
823                left: vec![],
824                right: vec![Tree::Item("+"), Tree::Item("b")],
825                path: Path::Top.into(),
826            }
827            .into(),
828            cursor: Tree::Item("a"),
829        }
830        .insert_right(Tree::Item("."));
831
832        let expect = Location {
833            path: Path::Node {
834                left: vec![],
835                right: vec![Tree::Item("."), Tree::Item("+"), Tree::Item("b")],
836                path: Path::Top.into(),
837            }
838            .into(),
839            cursor: Tree::Item("a"),
840        }
841        .into();
842
843        assert_eq!(result, expect);
844    }
845
846    #[test]
847    fn test_insert_down() {
848        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
849
850        let location = Location {
851            path: Path::Top.into(),
852            cursor: tree,
853        };
854
855        let new_tree = Tree::Item("-");
856        let updated_location = location.insert_down(new_tree);
857
858        assert_eq!(
859            updated_location,
860            Some(Location {
861                cursor: Tree::Item("-"),
862                path: Path::Node {
863                    left: [].into(),
864                    right: vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")],
865                    path: crate::Path::Top.into(),
866                }
867                .into()
868            })
869        );
870    }
871
872    #[test]
873    fn test_insert_down_none() {
874        let location = Location {
875            path: Path::Node {
876                left: vec![Tree::Item("a")],
877                right: vec![Tree::Item("b")],
878                path: Path::Top.into(),
879            }
880            .into(),
881            cursor: Tree::Item("+"),
882        };
883
884        let new_tree = Tree::Item("-");
885        let updated_location = location.insert_down(new_tree);
886
887        assert_eq!(updated_location, None);
888    }
889
890    #[test]
891    fn test_delete_top() {
892        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
893
894        let location = Location {
895            path: Path::Top.into(),
896            cursor: tree,
897        };
898
899        assert_eq!(location.delete(), None);
900    }
901
902    #[test]
903    fn test_delete_middle_node() {
904        let location = Location {
905            path: Path::Node {
906                left: vec![Tree::Item("+"), Tree::Item("a")],
907                right: vec![],
908                path: Path::Top.into(),
909            }
910            .into(),
911            cursor: Tree::Item("b"),
912        };
913
914        let updated_location = location.delete();
915
916        assert_eq!(
917            updated_location,
918            Some(Location {
919                cursor: Tree::Item("+"),
920                path: Path::Node {
921                    left: [Tree::Item("a")].into(),
922                    right: [].into(),
923                    path: crate::Path::Top.into(),
924                }
925                .into()
926            })
927        );
928    }
929
930    #[test]
931    fn test_delete_last_node() {
932        let tree = Tree::Section(vec![Tree::Item("a"), Tree::Item("+"), Tree::Item("b")]);
933
934        let location = Location {
935            path: Path::Top.into(),
936            cursor: tree,
937        };
938
939        let updated_location = location.go_down().and_then(Location::delete);
940
941        assert_eq!(
942            updated_location,
943            Some(Location {
944                cursor: Tree::Item("+"),
945                path: Path::Node {
946                    right: [Tree::Item("b")].into(),
947                    left: [].into(),
948                    path: crate::Path::Top.into(),
949                }
950                .into()
951            })
952        );
953    }
954
955    #[test]
956    fn test_delete_only_child() {
957        let tree = Tree::Section(vec![Tree::Item("a")]);
958
959        let location = Location {
960            path: Path::Top.into(),
961            cursor: tree,
962        };
963
964        let updated_location = location.go_down().and_then(Location::delete);
965
966        assert_eq!(
967            updated_location,
968            Some(Location {
969                cursor: Tree::Section(vec![]),
970                path: crate::Path::Top.into(),
971            })
972        );
973    }
974}