generic_cursors/
simple.rs

1use std::{future::Future, marker::PhantomData, pin::Pin};
2
3pub struct MutRefStack<'root, T: ?Sized> {
4    /// Ensures this mutrefstack does not exceed the lifetime of its root.
5    lifetime: PhantomData<&'root mut T>,
6    /// The stack of pointers. Each one borrows from the one prior, except the first which is the `root` and may never be popped.
7    data: Vec<*mut T>,
8}
9
10pub enum MoveDecision<'root, 'this, T: ?Sized> {
11    Ascend,
12    Stay,
13    Descend(&'this mut T),
14    Inject(&'root mut T),
15}
16
17pub enum MoveError {
18    AscendAtRoot,
19}
20
21impl<'root, T: ?Sized> MutRefStack<'root, T> {
22    /// Create a new MutRefStack from a mutable reference to the root
23    /// of a recursive data structure.
24    pub fn new(root: &'root mut T) -> Self {
25        Self {
26            lifetime: PhantomData,
27            data: vec![root],
28        }
29    }
30
31    /// Helper function to get the raw top pointer.
32    fn raw_top(&self) -> *mut T {
33        self.data
34            .last()
35            .copied()
36            .expect("root pointer should never be popped")
37    }
38
39    /// Obtain a shared reference to the top of the stack.
40    pub fn top(&self) -> &T {
41        let ptr: *const T = self.raw_top();
42        unsafe { &(*ptr) }
43    }
44
45    /// Obtain a mutable reference to the top of the stack.
46    pub fn top_mut(&mut self) -> &mut T {
47        let ptr: *mut T = self.raw_top();
48        unsafe { &mut (*ptr) }
49    }
50
51    /// Is this MutRefStack currently at its root?
52    pub fn is_at_root(&self) -> bool {
53        self.data.len() == 1
54    }
55
56    /// Inject a new reference to the top of the stack. The reference still must live
57    /// as long as the root of the stack.
58    pub fn inject_top(&mut self, new_top: &'root mut T) -> &mut T {
59        self.data.push(new_top);
60        self.top_mut()
61    }
62
63    /// Inject a new reference to the top of the stack. The reference still must live
64    /// as long as the root of the stack.
65    pub fn inject_with(
66        &mut self,
67        f: impl FnOnce(&mut T) -> Option<&'root mut T>,
68    ) -> Option<&mut T> {
69        let new_top = f(self.top_mut())?;
70        self.data.push(new_top);
71        Some(self.top_mut())
72    }
73
74    /// Descend into the recursive data structure, returning a mutable reference to the new top element.
75    /// Rust's borrow checker enforces that the closure cannot inject any lifetime (other than `'static`),
76    /// because the closure must work for any lifetime `'node`.
77    pub fn descend_with(
78        &mut self,
79        f: impl for<'node> FnOnce(&'node mut T) -> Option<&'node mut T>,
80    ) -> Option<&mut T> {
81        let old_top: *mut T = self.raw_top();
82        let new_top: &mut T = unsafe { f(&mut *old_top)? };
83        self.data.push(new_top);
84        Some(new_top)
85    }
86
87    /// Ascend back up from the recursive data structure, returning a mutable reference to the new top element, if it changed.
88    /// If we are not currently at the root, ascend and return a reference to the new top.
89    /// If we are already at the root, returns None (the top is the root and does not change).
90    pub fn ascend(&mut self) -> Option<&mut T> {
91        match self.data.len() {
92            0 => unreachable!("root pointer must always exist"),
93            1 => None,
94            _ => {
95                self.data.pop();
96                Some(self.top_mut())
97            }
98        }
99    }
100
101    /// Ascend back up from the recursive data structure while the given closure returns `true`, returning a mutable reference to the new top element.
102    /// If we are not currently at the root, and the predicate returns `true`, ascend and continue.
103    /// If we are already at the root, or if the predicate returned false, returns a reference to the top element.
104    pub fn ascend_while<P>(&mut self, mut predicate: P) -> &mut T
105    where
106        P: FnMut(&mut T) -> bool,
107    {
108        while !self.is_at_root() && predicate(self.top_mut()) {
109            let Some(_) = self.ascend() else {
110                unreachable!();
111            };
112        }
113        self.top_mut()
114    }
115
116    /// Ascend from, descend from, inject a new stack top, or stay at the current node,
117    /// based on the return value of the closure.
118    pub fn move_with<F>(&mut self, f: F) -> Result<&mut T, MoveError>
119    where
120        F: for<'a> FnOnce(&'a mut T) -> MoveDecision<'root, 'a, T>,
121    {
122        let top = self.top_mut();
123        let result = f(top);
124        match result {
125            MoveDecision::Ascend => self.ascend().ok_or(MoveError::AscendAtRoot),
126            MoveDecision::Stay => Ok(self.top_mut()),
127            MoveDecision::Inject(new_top) | MoveDecision::Descend(new_top) => {
128                let new_top: *mut T = new_top;
129                self.data.push(new_top);
130                Ok(self.top_mut())
131            }
132        }
133    }
134
135    pub async fn move_with_async<F>(&mut self, f: F) -> Result<&mut T, MoveError>
136    where
137        F: for<'a> FnOnce(
138            &'a mut T,
139        )
140            -> Pin<Box<dyn Future<Output = MoveDecision<'root, 'a, T>> + 'a>>,
141    {
142        let top = self.top_mut();
143        let result = f(top).await;
144        match result {
145            MoveDecision::Ascend => self.ascend().ok_or(MoveError::AscendAtRoot),
146            MoveDecision::Stay => Ok(self.top_mut()),
147            MoveDecision::Inject(new_top) | MoveDecision::Descend(new_top) => {
148                let new_top: *mut T = new_top;
149                self.data.push(new_top);
150                Ok(self.top_mut())
151            }
152        }
153    }
154
155    /// Return reference to the top element of this stack, forgetting about the stack entirely.
156    pub fn into_top(self) -> &'root mut T {
157        let ptr = self.data.last().copied().unwrap();
158        unsafe { &mut *ptr }
159    }
160
161    /// Pop all references off the stack and go back to the root.
162    pub fn to_root(&mut self) -> &mut T {
163        self.data.truncate(1);
164        self.top_mut()
165    }
166}