aam_core/pipeline/
scope_manager.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum BlockType {
8 None,
10 Object,
12 List,
14 DirectiveBlock,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum ScopeError {
20 UnbalancedExit,
21}
22
23#[derive(Debug)]
25pub struct ScopeManager {
26 nesting_depth: i32,
28 current_block: BlockType,
30 block_stack: Vec<BlockType>,
32 accumulated_content: String,
34}
35
36impl ScopeManager {
37 #[must_use]
39 pub const fn new() -> Self {
40 Self {
41 nesting_depth: 0,
42 current_block: BlockType::None,
43 block_stack: Vec::new(),
44 accumulated_content: String::new(),
45 }
46 }
47
48 #[must_use]
50 pub const fn nesting_depth(&self) -> i32 {
51 self.nesting_depth
52 }
53
54 #[must_use]
56 pub const fn current_block(&self) -> BlockType {
57 self.current_block
58 }
59
60 #[must_use]
62 pub const fn in_nested_context(&self) -> bool {
63 self.nesting_depth > 0
64 }
65
66 pub fn enter_block(&mut self, block_type: BlockType) {
68 self.block_stack.push(self.current_block);
69 self.current_block = block_type;
70 self.nesting_depth += 1;
71 }
72
73 pub fn exit_block(&mut self) -> Result<(), ScopeError> {
83 if self.nesting_depth <= 0 {
84 return Err(ScopeError::UnbalancedExit);
85 }
86 self.nesting_depth -= 1;
87 self.current_block = self.block_stack.pop().unwrap_or(BlockType::None);
88 Ok(())
89 }
90
91 #[must_use]
93 pub fn accumulated_content(&self) -> &str {
94 &self.accumulated_content
95 }
96
97 pub fn accumulate(&mut self, text: &str) {
99 if !self.accumulated_content.is_empty() {
100 self.accumulated_content.push(' ');
101 }
102 self.accumulated_content.push_str(text);
103 }
104
105 pub fn clear_accumulated(&mut self) {
107 self.accumulated_content.clear();
108 }
109
110 #[must_use]
112 pub const fn block_is_complete(&self) -> bool {
113 self.nesting_depth == 0 && !self.accumulated_content.is_empty()
114 }
115
116 pub fn reset(&mut self) {
118 self.nesting_depth = 0;
119 self.current_block = BlockType::None;
120 self.block_stack.clear();
121 self.accumulated_content.clear();
122 }
123}
124
125impl Default for ScopeManager {
126 fn default() -> Self {
127 Self::new()
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_basic_nesting() {
137 let mut scope = ScopeManager::new();
138 assert_eq!(scope.nesting_depth(), 0);
139 assert!(!scope.in_nested_context());
140
141 scope.enter_block(BlockType::Object);
142 assert_eq!(scope.nesting_depth(), 1);
143 assert!(scope.in_nested_context());
144 assert_eq!(scope.current_block(), BlockType::Object);
145
146 scope.exit_block().unwrap();
147 assert_eq!(scope.nesting_depth(), 0);
148 assert!(!scope.in_nested_context());
149 }
150
151 #[test]
152 fn test_nested_objects() {
153 let mut scope = ScopeManager::new();
154 scope.enter_block(BlockType::Object);
155 scope.enter_block(BlockType::Object);
156 assert_eq!(scope.nesting_depth(), 2);
157
158 scope.exit_block().unwrap();
159 assert_eq!(scope.current_block(), BlockType::Object);
160
161 scope.exit_block().unwrap();
162 assert_eq!(scope.current_block(), BlockType::None);
163 }
164
165 #[test]
166 fn test_exit_without_enter() {
167 let mut scope = ScopeManager::new();
168 assert!(scope.exit_block().is_err());
169 }
170
171 #[test]
172 fn test_accumulation() {
173 let mut scope = ScopeManager::new();
174 assert_eq!(scope.accumulated_content(), "");
175
176 scope.accumulate("first");
177 assert_eq!(scope.accumulated_content(), "first");
178
179 scope.accumulate("second");
180 assert_eq!(scope.accumulated_content(), "first second");
181
182 scope.clear_accumulated();
183 assert_eq!(scope.accumulated_content(), "");
184 }
185
186 #[test]
187 fn test_block_complete() {
188 let mut scope = ScopeManager::new();
189 assert!(!scope.block_is_complete());
190
191 scope.accumulate("content");
192 assert!(scope.block_is_complete());
193
194 scope.clear_accumulated();
195 assert!(!scope.block_is_complete());
196 }
197}