mf_transform/
batch_step.rs1use std::sync::Arc;
2
3use mf_model::{schema::Schema, tree::Tree};
4
5use crate::{transform_error, TransformResult};
6
7use super::step::{Step, StepResult};
8
9#[derive(Debug, Clone)]
13pub struct BatchStep {
14 pub steps: Vec<Arc<dyn Step>>,
15}
16
17impl BatchStep {
18 pub fn new(steps: Vec<Arc<dyn Step>>) -> Self {
19 Self { steps }
20 }
21}
22
23impl Step for BatchStep {
24 fn name(&self) -> String {
25 "batch_step".to_string()
26 }
27
28 fn apply(
29 &self,
30 dart: &mut Tree,
31 schema: Arc<Schema>,
32 ) -> TransformResult<StepResult> {
33 let mut inverses: Vec<Arc<dyn Step>> =
36 Vec::with_capacity(self.steps.len());
37
38 for step in &self.steps {
39 if let Some(inv) = step.invert(&Arc::new(dart.clone())) {
41 inverses.push(inv);
42 } else {
43 inverses.push(Arc::new(crate::attr_step::AttrStep::new(
44 "__invalid__".into(),
47 imbl::hashmap! {},
48 )));
49 }
50
51 match step.apply(dart, schema.clone()) {
53 Ok(res) => {
54 if let Some(message) = res.failed {
55 for inv in inverses.into_iter().rev() {
57 let _ = inv.apply(dart, schema.clone());
58 }
59 return Err(transform_error(message));
60 }
61 },
62 Err(e) => {
63 for inv in inverses.into_iter().rev() {
65 let _ = inv.apply(dart, schema.clone());
66 }
67 return Err(e);
68 },
69 }
70 }
71
72 Ok(StepResult::ok())
73 }
74
75 fn serialize(&self) -> Option<Vec<u8>> {
76 None
78 }
79
80 fn invert(
81 &self,
82 dart: &Arc<Tree>,
83 ) -> Option<Arc<dyn Step>> {
84 let mut invs: Vec<Arc<dyn Step>> = Vec::new();
87 for step in &self.steps {
88 if let Some(inv) = step.invert(dart) {
89 invs.push(inv);
90 }
91 }
92 if invs.is_empty() {
93 None
94 } else {
95 invs.reverse();
96 Some(Arc::new(BatchStep::new(invs)))
97 }
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::{attr_step::AttrStep, node_step::AddNodeStep};
105 use mf_model::{
106 attrs::Attrs,
107 node::Node,
108 node_type::{NodeEnum, NodeSpec},
109 schema::{Schema, SchemaSpec},
110 tree::Tree,
111 };
112 use serde_json::json;
113 use std::collections::HashMap;
114
115 fn create_schema() -> Arc<Schema> {
116 let mut nodes = HashMap::new();
117 nodes.insert(
118 "doc".to_string(),
119 NodeSpec {
120 content: None,
121 marks: None,
122 group: None,
123 desc: None,
124 attrs: None,
125 },
126 );
127 let spec = SchemaSpec {
128 nodes,
129 marks: HashMap::new(),
130 top_node: Some("doc".to_string()),
131 };
132 Arc::new(Schema::compile(spec).unwrap())
133 }
134
135 #[test]
136 fn batch_step_apply_and_invert() {
137 let schema = create_schema();
138 let root = Node::new(
139 "doc",
140 "doc".to_string(),
141 Attrs::default(),
142 vec![],
143 vec![],
144 );
145 let mut tree = Tree::new(root);
146
147 let child = Node::new(
149 "n1",
150 "doc".to_string(),
151 Attrs::default(),
152 vec![],
153 vec![],
154 );
155 let add = Arc::new(AddNodeStep::new(
156 "doc".into(),
157 vec![NodeEnum(child, vec![])],
158 ));
159 let set = Arc::new(AttrStep::new(
160 "n1".into(),
161 imbl::hashmap! {"k".into()=>json!(1)},
162 ));
163
164 let batch = BatchStep::new(vec![add, set]);
165 let res = batch.apply(&mut tree, schema.clone());
166 assert!(res.is_ok());
167
168 let inv = batch.invert(&Arc::new(tree.clone())).unwrap();
170 let r = inv.apply(&mut tree, schema);
171 assert!(r.is_ok());
172 }
173}