Skip to main content

pdf_ast/transform/
operations.rs

1use super::*;
2use crate::ast::{AstNode, NodeId, PdfAstGraph};
3use crate::types::PdfValue;
4
5/// Transform operation that can be applied to AST nodes
6#[derive(Debug, Clone)]
7pub enum TransformOperation {
8    /// Replace a node with a new node
9    ReplaceNode { target: NodeId, new_node: AstNode },
10    /// Insert a new node as a child
11    InsertChild {
12        parent: NodeId,
13        child: AstNode,
14        position: Option<usize>,
15    },
16    /// Remove a node
17    RemoveNode {
18        target: NodeId,
19        preserve_children: bool,
20    },
21    /// Move a node to a new parent
22    MoveNode {
23        target: NodeId,
24        new_parent: NodeId,
25        position: Option<usize>,
26    },
27    /// Update node value
28    UpdateValue { target: NodeId, new_value: PdfValue },
29    /// Batch operation containing multiple operations
30    Batch(Vec<TransformOperation>),
31}
32
33impl TransformOperation {
34    /// Apply this operation to the graph
35    pub fn apply(&self, graph: &mut PdfAstGraph) -> AstResult<()> {
36        match self {
37            TransformOperation::ReplaceNode { target, new_node } => {
38                graph.replace_node(*target, new_node.clone())?;
39            }
40            TransformOperation::InsertChild {
41                parent,
42                child,
43                position,
44            } => {
45                let child_id = graph.create_node(child.node_type.clone(), child.value.clone());
46                graph.add_edge(*parent, child_id, crate::ast::EdgeType::Child);
47
48                // TODO: Handle position parameter for ordered insertion
49                let _ = position;
50            }
51            TransformOperation::RemoveNode {
52                target,
53                preserve_children,
54            } => {
55                if *preserve_children {
56                    // Move children to parent before removing
57                    let children = graph.get_children(*target);
58                    if let Some(parent_id) = graph.get_parent(*target) {
59                        for child_id in children {
60                            graph.remove_edge(*target, child_id);
61                            graph.add_edge(parent_id, child_id, crate::ast::EdgeType::Child);
62                        }
63                    }
64                }
65                graph.remove_node(*target);
66            }
67            TransformOperation::MoveNode {
68                target,
69                new_parent,
70                position,
71            } => {
72                // Remove from current parent
73                if let Some(old_parent) = graph.get_parent(*target) {
74                    graph.remove_edge(old_parent, *target);
75                }
76
77                // Add to new parent
78                graph.add_edge(*new_parent, *target, crate::ast::EdgeType::Child);
79
80                // TODO: Handle position parameter
81                let _ = position;
82            }
83            TransformOperation::UpdateValue { target, new_value } => {
84                if let Some(node) = graph.get_node_mut(*target) {
85                    node.value = new_value.clone();
86                } else {
87                    return Err(AstError::NodeNotFound(format!("Node {:?}", target)));
88                }
89            }
90            TransformOperation::Batch(operations) => {
91                for operation in operations {
92                    operation.apply(graph)?;
93                }
94            }
95        }
96        Ok(())
97    }
98
99    /// Create a replace operation
100    pub fn replace(target: NodeId, new_node: AstNode) -> Self {
101        TransformOperation::ReplaceNode { target, new_node }
102    }
103
104    /// Create an insert operation
105    pub fn insert(parent: NodeId, child: AstNode) -> Self {
106        TransformOperation::InsertChild {
107            parent,
108            child,
109            position: None,
110        }
111    }
112
113    /// Create an insert operation at specific position
114    pub fn insert_at(parent: NodeId, child: AstNode, position: usize) -> Self {
115        TransformOperation::InsertChild {
116            parent,
117            child,
118            position: Some(position),
119        }
120    }
121
122    /// Create a remove operation
123    pub fn remove(target: NodeId) -> Self {
124        TransformOperation::RemoveNode {
125            target,
126            preserve_children: false,
127        }
128    }
129
130    /// Create a remove operation that preserves children
131    pub fn remove_preserve_children(target: NodeId) -> Self {
132        TransformOperation::RemoveNode {
133            target,
134            preserve_children: true,
135        }
136    }
137
138    /// Create a move operation
139    pub fn move_node(target: NodeId, new_parent: NodeId) -> Self {
140        TransformOperation::MoveNode {
141            target,
142            new_parent,
143            position: None,
144        }
145    }
146
147    /// Create a move operation to specific position
148    pub fn move_to_position(target: NodeId, new_parent: NodeId, position: usize) -> Self {
149        TransformOperation::MoveNode {
150            target,
151            new_parent,
152            position: Some(position),
153        }
154    }
155
156    /// Create an update value operation
157    pub fn update_value(target: NodeId, new_value: PdfValue) -> Self {
158        TransformOperation::UpdateValue { target, new_value }
159    }
160
161    /// Create a batch operation
162    pub fn batch(operations: Vec<TransformOperation>) -> Self {
163        TransformOperation::Batch(operations)
164    }
165}