beetry_node/control/
sequence.rs1use beetry_core::{Node, NonEmptyNodes, TickStatus};
2
3use crate::{Indices, control::RunningNodesAborter};
4
5pub struct Sequence {
17 nodes: NonEmptyNodes,
18 aborter: RunningNodesAborter,
19}
20
21impl Sequence {
22 #[must_use]
23 pub fn new(nodes: impl Into<NonEmptyNodes>) -> Self {
24 Self {
25 nodes: nodes.into(),
26 aborter: RunningNodesAborter::new(),
27 }
28 }
29}
30
31impl Node for Sequence {
32 fn tick(&mut self) -> TickStatus {
33 let aborter = &mut self.aborter;
34 for idx in self.nodes.indices() {
35 let node = &mut self.nodes[idx];
36 match node.tick() {
37 TickStatus::Success => {
38 aborter.untrack(idx);
39 }
40 TickStatus::Running => {
41 aborter.abort_if_other_running(&mut self.nodes, idx);
42 aborter.track(idx);
43 return TickStatus::Running;
44 }
45
46 TickStatus::Failure => {
47 aborter.abort_if_other_running(&mut self.nodes, idx);
48 return TickStatus::Failure;
49 }
50 }
51 }
52 TickStatus::Success
53 }
54
55 fn abort(&mut self) {
56 self.aborter.clear();
57 for node in &mut self.nodes {
58 node.abort();
59 }
60 }
61
62 fn reset(&mut self) {
63 self.aborter.clear();
64 for node in &mut self.nodes {
65 node.reset();
66 }
67 }
68}
69
70pub struct MemorySequence {
75 nodes: NonEmptyNodes,
76 running_idx: Option<usize>,
77}
78
79impl MemorySequence {
80 pub fn new(nodes: impl Into<NonEmptyNodes>) -> Self {
81 Self {
82 nodes: nodes.into(),
83 running_idx: None,
84 }
85 }
86}
87
88impl Node for MemorySequence {
89 fn tick(&mut self) -> TickStatus {
90 let start_idx = self.running_idx.take().unwrap_or(0);
91 for idx in start_idx..self.nodes.len().into() {
92 let node = &mut self.nodes[idx];
93 match node.tick() {
94 TickStatus::Success => {}
95 TickStatus::Running => {
96 self.running_idx = Some(idx);
97 return TickStatus::Running;
98 }
99 TickStatus::Failure => {
100 return TickStatus::Failure;
101 }
102 }
103 }
104 TickStatus::Success
105 }
106
107 fn abort(&mut self) {
108 self.running_idx = None;
109 for node in &mut self.nodes {
110 node.abort();
111 }
112 }
113
114 fn reset(&mut self) {
115 self.running_idx = None;
116 for node in &mut self.nodes {
117 node.reset();
118 }
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use beetry_core::{Node, TickStatus};
125
126 use super::*;
127 use crate::mock_test::{boxed, mock_returns};
128
129 #[test]
130 fn success_with_all_success() {
131 let nodes = NonEmptyNodes::from([
132 boxed(mock_returns([TickStatus::Success])),
133 boxed(mock_returns([TickStatus::Success])),
134 ]);
135 let mut sq = Sequence::new(nodes);
136
137 assert_eq!(sq.tick(), TickStatus::Success);
138 }
139
140 #[test]
141 fn running_with_first_running() {
142 let nodes = NonEmptyNodes::from([
143 boxed(mock_returns([TickStatus::Success])),
144 boxed(mock_returns([TickStatus::Running])),
145 boxed(mock_returns([])),
146 ]);
147 let mut sq = Sequence::new(nodes);
148 assert_eq!(sq.tick(), TickStatus::Running);
149 }
150
151 #[test]
152 fn failure_with_first_failed() {
153 let nodes = NonEmptyNodes::from([
154 boxed(mock_returns([TickStatus::Success])),
155 boxed(mock_returns([TickStatus::Failure])),
156 boxed(mock_returns([])),
157 ]);
158 let mut sq = Sequence::new(nodes);
159
160 assert_eq!(sq.tick(), TickStatus::Failure);
161 }
162
163 #[test]
164 fn resets_running() {
165 let m1 = mock_returns([
166 TickStatus::Success,
167 TickStatus::Success,
168 TickStatus::Running,
169 ]);
170 let mut m2 = mock_returns([TickStatus::Success, TickStatus::Running]);
171 let mut m3 = mock_returns([TickStatus::Running]);
172 m2.expect_abort().once().return_const(());
173 m3.expect_abort().once().return_const(());
174
175 let nodes = NonEmptyNodes::from([boxed(m1), boxed(m2), boxed(m3)]);
176 let mut sq = Sequence::new(nodes);
177
178 assert_eq!(sq.tick(), TickStatus::Running);
179 assert_eq!(sq.tick(), TickStatus::Running);
180 assert_eq!(sq.tick(), TickStatus::Running);
181 }
182
183 #[test]
184 fn mem_sequence_resumes_from_running_child() {
185 let m1 = mock_returns([TickStatus::Success]);
186 let m2 = mock_returns([
187 TickStatus::Running,
188 TickStatus::Running,
189 TickStatus::Success,
190 ]);
191 let m3 = mock_returns([TickStatus::Success]);
192
193 let nodes = NonEmptyNodes::from([boxed(m1), boxed(m2), boxed(m3)]);
194 let mut msq = MemorySequence::new(nodes);
195
196 assert_eq!(msq.tick(), TickStatus::Running);
197 assert_eq!(msq.tick(), TickStatus::Running);
198 assert_eq!(msq.tick(), TickStatus::Success);
199 }
200
201 #[test]
202 fn mem_sequence_reset_clears_memory() {
203 let mut m1 = mock_returns([TickStatus::Success, TickStatus::Success]);
204 m1.expect_reset().once().return_const(());
205 let mut m2 = mock_returns([TickStatus::Running, TickStatus::Running]);
206 m2.expect_reset().once().return_const(());
207
208 let nodes = NonEmptyNodes::from([boxed(m1), boxed(m2)]);
209 let mut msq = MemorySequence::new(nodes);
210
211 assert_eq!(msq.tick(), TickStatus::Running);
212 msq.reset();
213 assert_eq!(msq.tick(), TickStatus::Running);
214 }
215}