Skip to main content

eure_tree/
action.rs

1use thiserror::Error;
2
3use crate::prelude::*;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum NodeTarget {
7    CstNodeId(CstNodeId),
8    CommandNodeId(CommandNodeId),
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct CommandNodeId(usize);
13
14impl From<CommandNodeId> for NodeTarget {
15    fn from(value: CommandNodeId) -> Self {
16        NodeTarget::CommandNodeId(value)
17    }
18}
19
20impl From<CstNodeId> for NodeTarget {
21    fn from(value: CstNodeId) -> Self {
22        NodeTarget::CstNodeId(value)
23    }
24}
25
26#[derive(Debug, PartialEq, Eq, Clone, Default)]
27pub struct CstCommands {
28    insert_num: usize,
29    commands: Vec<Command>,
30}
31
32impl CstCommands {
33    pub fn delete_node(&mut self, id: impl Into<NodeTarget>) {
34        self.commands.push(Command::DeleteNode(id.into()));
35    }
36
37    pub fn delete_recursive(&mut self, id: impl Into<NodeTarget>) {
38        self.commands.push(Command::DeleteRecursive(id.into()));
39    }
40
41    #[must_use]
42    pub fn insert_dynamic_terminal(
43        &mut self,
44        kind: TerminalKind,
45        data: impl Into<String>,
46    ) -> CommandNodeId {
47        self.commands.push(Command::InsertDynamicTerminal {
48            kind,
49            data: data.into(),
50        });
51        let id = CommandNodeId(self.insert_num);
52        self.insert_num += 1;
53        id
54    }
55
56    #[must_use]
57    pub fn insert_node(&mut self, data: CstNode) -> CommandNodeId {
58        self.commands.push(Command::Insert { data });
59        let id = CommandNodeId(self.insert_num);
60        self.insert_num += 1;
61        id
62    }
63
64    pub fn update_node(&mut self, id: impl Into<NodeTarget>, data: CstNode) {
65        self.commands.push(Command::Update {
66            id: id.into(),
67            data,
68        });
69    }
70
71    pub fn update_children(
72        &mut self,
73        id: impl Into<NodeTarget>,
74        children: impl IntoIterator<Item = impl Into<NodeTarget>>,
75    ) {
76        self.commands.push(Command::UpdateChildren {
77            id: id.into(),
78            children: children.into_iter().map(|c| c.into()).collect(),
79        });
80    }
81
82    /// Add nodes before the target child node
83    pub fn add_nodes_before(
84        &mut self,
85        id: impl Into<NodeTarget>,
86        before: impl Into<NodeTarget>,
87        data: impl IntoIterator<Item = impl Into<NodeTarget>>,
88    ) {
89        self.commands.push(Command::AddNodesBefore {
90            id: id.into(),
91            before: before.into(),
92            data: data.into_iter().map(|d| d.into()).collect(),
93        });
94    }
95
96    /// Add nodes after the target child node
97    pub fn add_nodes_after(
98        &mut self,
99        id: impl Into<NodeTarget>,
100        after: impl Into<NodeTarget>,
101        data: impl IntoIterator<Item = impl Into<NodeTarget>>,
102    ) {
103        self.commands.push(Command::AddNodesAfter {
104            id: id.into(),
105            after: after.into(),
106            data: data.into_iter().map(|d| d.into()).collect(),
107        });
108    }
109}
110
111#[derive(Debug, Error)]
112pub enum CommandApplyError {
113    #[error("before node not found")]
114    BeforeNodeNotFound { id: CstNodeId, before: CstNodeId },
115}
116
117impl CstCommands {
118    pub fn apply_to(self, tree: &mut Cst) -> Result<(), CommandApplyError> {
119        let mut inserted = vec![];
120        let to_id = |inserted: &[CstNodeId], target: NodeTarget| -> CstNodeId {
121            match target {
122                NodeTarget::CstNodeId(id) => id,
123                NodeTarget::CommandNodeId(id) => inserted[id.0],
124            }
125        };
126        for command in self.commands.into_iter() {
127            match command {
128                Command::Insert { data } => {
129                    let id = tree.add_node(data);
130                    inserted.push(id);
131                }
132                Command::DeleteNode(node_target) => {
133                    tree.remove_node(to_id(&inserted, node_target));
134                }
135                Command::DeleteRecursive(node_target) => {
136                    tree.remove_node(to_id(&inserted, node_target));
137                }
138                Command::ChangeParent { id, parent } => {
139                    tree.change_parent(to_id(&inserted, id), to_id(&inserted, parent));
140                }
141                Command::Update { id, data } => {
142                    tree.update_node(to_id(&inserted, id), data);
143                }
144                Command::UpdateChildren { id, children } => {
145                    tree.update_children(
146                        to_id(&inserted, id),
147                        children.into_iter().map(|c| to_id(&inserted, c)),
148                    );
149                }
150                Command::AddNodesBefore { id, before, data } => {
151                    let mut children = tree.children(to_id(&inserted, id)).collect::<Vec<_>>();
152                    let Some(before_index) =
153                        children.iter().position(|c| to_id(&inserted, before) == *c)
154                    else {
155                        return Err(CommandApplyError::BeforeNodeNotFound {
156                            id: to_id(&inserted, id),
157                            before: to_id(&inserted, before),
158                        });
159                    };
160                    children.splice(
161                        before_index..before_index,
162                        data.into_iter().map(|d| to_id(&inserted, d)),
163                    );
164                    tree.update_children(to_id(&inserted, id), children);
165                }
166                Command::AddNodesAfter { id, after, data } => {
167                    let mut children = tree.children(to_id(&inserted, id)).collect::<Vec<_>>();
168                    let Some(after_index) =
169                        children.iter().position(|c| to_id(&inserted, after) == *c)
170                    else {
171                        return Err(CommandApplyError::BeforeNodeNotFound {
172                            id: to_id(&inserted, id),
173                            before: to_id(&inserted, after),
174                        });
175                    };
176                    children.splice(
177                        (after_index + 1)..(after_index + 1),
178                        data.into_iter().map(|d| to_id(&inserted, d)),
179                    );
180                    tree.update_children(to_id(&inserted, id), children);
181                }
182                Command::InsertDynamicTerminal { kind, data } => {
183                    let token_id = tree.insert_dynamic_terminal(data);
184                    let node_id = tree.add_node(CstNode::Terminal {
185                        kind,
186                        data: TerminalData::Dynamic(token_id),
187                    });
188                    inserted.push(node_id);
189                }
190            }
191        }
192        Ok(())
193    }
194}
195
196#[derive(Debug, Clone, PartialEq, Eq)]
197pub enum Command {
198    DeleteNode(NodeTarget),
199    DeleteRecursive(NodeTarget),
200    ChangeParent {
201        id: NodeTarget,
202        parent: NodeTarget,
203    },
204    Insert {
205        data: CstNode,
206    },
207    Update {
208        id: NodeTarget,
209        data: CstNode,
210    },
211    UpdateChildren {
212        id: NodeTarget,
213        children: Vec<NodeTarget>,
214    },
215    AddNodesBefore {
216        id: NodeTarget,
217        before: NodeTarget,
218        data: Vec<NodeTarget>,
219    },
220    AddNodesAfter {
221        id: NodeTarget,
222        after: NodeTarget,
223        data: Vec<NodeTarget>,
224    },
225    InsertDynamicTerminal {
226        kind: TerminalKind,
227        data: String,
228    },
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234    use crate::node_kind::{NonTerminalKind, TerminalKind};
235    use crate::tree::{
236        ConcreteSyntaxTree, CstNodeData, CstNodeId, DynamicTokenId, NonTerminalData,
237    };
238    fn create_test_tree() -> ConcreteSyntaxTree<TerminalKind, NonTerminalKind> {
239        let root_data =
240            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
241        ConcreteSyntaxTree::new(root_data)
242    }
243
244    #[test]
245    fn test_delete_node() {
246        let mut tree = create_test_tree();
247        let root = tree.root();
248
249        // Add a child node
250        let node_data =
251            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
252        let child = tree.add_node_with_parent(node_data, root);
253
254        let mut commands = CstCommands::default();
255        commands.delete_node(child);
256
257        commands.apply_to(&mut tree).unwrap();
258
259        assert!(tree.has_no_children(root));
260    }
261
262    #[test]
263    fn test_delete_recursive() {
264        let mut tree = create_test_tree();
265        let root = tree.root();
266
267        // Add a child node
268        let node_data =
269            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
270        let child = tree.add_node_with_parent(node_data, root);
271
272        // Add a grandchild node
273        let grandchild_data =
274            CstNodeData::new_non_terminal(NonTerminalKind::Eure, NonTerminalData::Dynamic);
275        let _grandchild = tree.add_node_with_parent(grandchild_data, child);
276
277        let mut commands = CstCommands::default();
278        commands.delete_node(child);
279
280        commands.apply_to(&mut tree).unwrap();
281
282        let root_children: Vec<_> = tree.children(root).collect();
283        assert!(!root_children.contains(&child));
284
285        let mut tree = create_test_tree();
286        let root = tree.root();
287
288        // Add a child node again
289        let node_data =
290            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
291        let child = tree.add_node_with_parent(node_data, root);
292
293        let mut commands = CstCommands::default();
294        commands.delete_recursive(child);
295
296        commands.apply_to(&mut tree).unwrap();
297
298        let root_children: Vec<_> = tree.children(root).collect();
299        assert!(!root_children.contains(&child));
300    }
301
302    #[test]
303    fn test_change_parent() {
304        let mut tree = create_test_tree();
305        let root = tree.root();
306
307        let node_data1 =
308            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
309        let child1 = tree.add_node_with_parent(node_data1, root);
310
311        let node_data2 =
312            CstNodeData::new_non_terminal(NonTerminalKind::Eure, NonTerminalData::Dynamic);
313        let child2 = tree.add_node_with_parent(node_data2, root);
314
315        let grandchild_data =
316            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
317        let grandchild = tree.add_node_with_parent(grandchild_data, child1);
318
319        let mut commands = CstCommands::default();
320        commands.commands.push(Command::ChangeParent {
321            id: grandchild.into(),
322            parent: child2.into(),
323        });
324
325        commands.apply_to(&mut tree).unwrap();
326
327        let child2_children: Vec<_> = tree.children(child2).collect();
328        assert!(child2_children.contains(&grandchild));
329    }
330
331    #[test]
332    fn test_insert() {
333        let mut tree = create_test_tree();
334        let root = tree.root();
335
336        let node_data =
337            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
338
339        let mut commands = CstCommands::default();
340        let node = commands.insert_node(node_data);
341        commands.update_children(root, vec![node]);
342
343        commands.apply_to(&mut tree).unwrap();
344
345        let children: Vec<_> = tree.children(root).collect();
346        assert_eq!(children.len(), 1);
347
348        let child_data = tree.node_data(children[0]).unwrap();
349        assert!(matches!(
350            child_data,
351            CstNodeData::NonTerminal {
352                kind: NonTerminalKind::Root,
353                ..
354            }
355        ));
356    }
357
358    #[test]
359    fn test_update() {
360        let mut tree = create_test_tree();
361        let root = tree.root();
362
363        // Add a child node
364        let node_data =
365            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
366        let child = tree.add_node_with_parent(node_data, root);
367
368        let new_data =
369            CstNodeData::new_non_terminal(NonTerminalKind::Eure, NonTerminalData::Dynamic);
370
371        let mut commands = CstCommands::default();
372        commands.update_node(child, new_data);
373
374        commands.apply_to(&mut tree).unwrap();
375
376        let updated_data = tree.node_data(child).unwrap();
377        assert!(matches!(
378            updated_data,
379            CstNodeData::NonTerminal {
380                kind: NonTerminalKind::Eure,
381                ..
382            }
383        ));
384    }
385
386    #[test]
387    fn test_add_nodes_before() {
388        let mut tree = create_test_tree();
389        let root = tree.root();
390
391        let node_data1 =
392            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
393        let child1 = tree.add_node_with_parent(node_data1, root);
394
395        let node_data2 =
396            CstNodeData::new_non_terminal(NonTerminalKind::Eure, NonTerminalData::Dynamic);
397        let child2 = tree.add_node_with_parent(node_data2, root);
398
399        let node_data3 =
400            CstNodeData::new_non_terminal(NonTerminalKind::Section, NonTerminalData::Dynamic);
401        let child3 = tree.add_node(node_data3);
402
403        let mut commands = CstCommands::default();
404        commands.add_nodes_before(root, child2, vec![child3]);
405
406        commands.apply_to(&mut tree).unwrap();
407
408        let children: Vec<_> = tree.children(root).collect();
409
410        assert_eq!(children.len(), 3);
411
412        assert!(children.contains(&child1));
413        assert!(children.contains(&child2));
414        assert!(children.contains(&child3));
415
416        assert!(tree.children(root).any(|id| id == child3));
417    }
418
419    #[test]
420    fn test_insert_dynamic_terminal() {
421        let mut tree = create_test_tree();
422        let root = tree.root();
423
424        let mut commands = CstCommands::default();
425        let token_id = commands.insert_dynamic_terminal(TerminalKind::Text, "test_text");
426        commands.update_children(root, vec![token_id]);
427
428        commands.apply_to(&mut tree).unwrap();
429
430        assert_eq!(tree.dynamic_token(DynamicTokenId(0)), Some("test_text"));
431    }
432
433    #[test]
434    fn test_insert_dynamic_terminal_with_add_nodes_before() {
435        let mut tree = create_test_tree();
436        let root = tree.root();
437
438        // Add two existing children to have something to insert between
439        let node_data1 =
440            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
441        let child1 = tree.add_node_with_parent(node_data1, root);
442
443        let node_data2 =
444            CstNodeData::new_non_terminal(NonTerminalKind::Eure, NonTerminalData::Dynamic);
445        let child2 = tree.add_node_with_parent(node_data2, root);
446
447        // Insert a dynamic terminal between child1 and child2
448        let mut commands = CstCommands::default();
449        let ws_node_id = commands.insert_dynamic_terminal(TerminalKind::Whitespace, " ");
450        commands.add_nodes_before(root, child2, vec![ws_node_id]);
451
452        commands.apply_to(&mut tree).unwrap();
453
454        let children: Vec<_> = tree.children(root).collect();
455        assert_eq!(children.len(), 3);
456
457        // Check that the whitespace was inserted between child1 and child2
458        let child1_pos = children.iter().position(|&id| id == child1).unwrap();
459        let child2_pos = children.iter().position(|&id| id == child2).unwrap();
460
461        // The whitespace should be between child1 and child2
462        assert!(child1_pos < child2_pos);
463
464        // Get the whitespace node (should be the one that's not child1 or child2)
465        let ws_node = children
466            .iter()
467            .find(|&&id| id != child1 && id != child2)
468            .unwrap();
469        let ws_data = tree.node_data(*ws_node).unwrap();
470
471        assert!(matches!(
472            ws_data,
473            CstNodeData::Terminal {
474                kind: TerminalKind::Whitespace,
475                data: TerminalData::Dynamic(_),
476            }
477        ));
478
479        // Check that the dynamic token was stored correctly
480        if let CstNodeData::Terminal {
481            data: TerminalData::Dynamic(token_id),
482            ..
483        } = ws_data
484        {
485            assert_eq!(tree.dynamic_token(token_id), Some(" "));
486        }
487    }
488
489    #[test]
490    fn test_commands_with_errors() {
491        let mut tree = create_test_tree();
492        let root = tree.root();
493
494        let invalid_node = CstNodeId(999);
495
496        let mut commands = CstCommands::default();
497        commands.delete_node(invalid_node);
498
499        assert!(commands.apply_to(&mut tree).is_ok());
500
501        let mut commands = CstCommands::default();
502        let node_data =
503            CstNodeData::new_non_terminal(NonTerminalKind::Root, NonTerminalData::Dynamic);
504        let child = tree.add_node(node_data);
505        commands.add_nodes_before(root, invalid_node, vec![child]);
506
507        let result = commands.apply_to(&mut tree);
508        assert!(result.is_err());
509    }
510}