mf_transform/
batch_step.rs

1use 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/// 批量步骤:将多个 Step 作为一个原子单元执行
10/// - 成功:全部子步骤成功应用
11/// - 失败:自动回滚已应用的子步骤,保证草稿一致性
12#[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        // 预先为每个子步骤生成回滚步骤(基于应用前的快照)
34        // 注意:为保证回滚正确性,这里在应用每个子步骤前都记录一次基线
35        let mut inverses: Vec<Arc<dyn Step>> =
36            Vec::with_capacity(self.steps.len());
37
38        for step in &self.steps {
39            // 基于当前草稿生成该步骤的反向操作
40            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                    // 占位的不可用反向步骤(不会被应用)
45                    // 这里不应发生,保持对齐
46                    "__invalid__".into(),
47                    imbl::hashmap! {},
48                )));
49            }
50
51            // 应用该子步骤
52            match step.apply(dart, schema.clone()) {
53                Ok(res) => {
54                    if let Some(message) = res.failed {
55                        // 失败,执行回滚
56                        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                    // 失败,执行回滚
64                    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        // 动态 Step 无法直接 serde 序列化,这里暂不支持
77        None
78    }
79
80    fn invert(
81        &self,
82        dart: &Arc<Tree>,
83    ) -> Option<Arc<dyn Step>> {
84        // 简化策略:对每个子步骤都基于同一基线计算反向,并逆序封装
85        // 注意:这与 Transform::apply_steps_batch 的预处理策略保持一致
86        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        // add + attr as a batch
148        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        // invert exists
169        let inv = batch.invert(&Arc::new(tree.clone())).unwrap();
170        let r = inv.apply(&mut tree, schema);
171        assert!(r.is_ok());
172    }
173}