use std::sync::Arc;
use mf_model::{schema::Schema, tree::Tree};
use mf_model::rpds::HashTrieMap;
use crate::{transform_error, TransformResult};
use super::step::{Step, StepResult};
#[derive(Debug, Clone)]
pub struct BatchStep {
pub steps: Vec<Arc<dyn Step>>,
}
impl BatchStep {
pub fn new(steps: Vec<Arc<dyn Step>>) -> Self {
Self { steps }
}
}
impl Step for BatchStep {
fn name(&self) -> String {
"batch_step".to_string()
}
fn apply(
&self,
dart: &mut Tree,
schema: Arc<Schema>,
) -> TransformResult<StepResult> {
let mut inverses: Vec<Arc<dyn Step>> =
Vec::with_capacity(self.steps.len());
for step in &self.steps {
if let Some(inv) = step.invert(&Arc::new(dart.clone())) {
inverses.push(inv);
} else {
inverses.push(Arc::new(crate::attr_step::AttrStep::new(
"__invalid__".into(),
HashTrieMap::new_sync(),
)));
}
match step.apply(dart, schema.clone()) {
Ok(res) => {
if let Some(message) = res.failed {
for inv in inverses.into_iter().rev() {
let _ = inv.apply(dart, schema.clone());
}
return Err(transform_error(message));
}
},
Err(e) => {
for inv in inverses.into_iter().rev() {
let _ = inv.apply(dart, schema.clone());
}
return Err(e);
},
}
}
Ok(StepResult::ok())
}
fn serialize(&self) -> Option<Vec<u8>> {
None
}
fn invert(
&self,
dart: &Arc<Tree>,
) -> Option<Arc<dyn Step>> {
let mut invs: Vec<Arc<dyn Step>> = Vec::new();
for step in &self.steps {
if let Some(inv) = step.invert(dart) {
invs.push(inv);
}
}
if invs.is_empty() {
None
} else {
invs.reverse();
Some(Arc::new(BatchStep::new(invs)))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{attr_step::AttrStep, node_step::AddNodeStep};
use mf_model::{
attrs::Attrs,
node::Node,
node_definition::{NodeTree, NodeSpec},
schema::{Schema, SchemaSpec},
tree::Tree,
};
use serde_json::json;
use std::collections::HashMap;
use mf_model::rpds::ht_map_sync;
fn create_schema() -> Arc<Schema> {
let mut nodes = HashMap::new();
nodes.insert(
"doc".to_string(),
NodeSpec {
content: None,
marks: None,
group: None,
desc: None,
attrs: None,
},
);
let spec = SchemaSpec {
nodes,
marks: HashMap::new(),
top_node: Some("doc".to_string()),
};
Arc::new(Schema::compile(spec).expect("测试 Schema 编译失败"))
}
#[test]
fn batch_step_apply_and_invert() {
let schema = create_schema();
let root = Node::new(
"doc",
"doc".to_string(),
Attrs::default(),
vec![],
vec![],
);
let mut tree = Tree::new(root);
let child = Node::new(
"n1",
"doc".to_string(),
Attrs::default(),
vec![],
vec![],
);
let add = Arc::new(AddNodeStep::new(
"doc".into(),
vec![NodeTree(child, vec![])],
));
let set = Arc::new(AttrStep::new(
"n1".into(),
ht_map_sync! ["k".into()=>json!(1)],
));
let batch = BatchStep::new(vec![add, set]);
let res = batch.apply(&mut tree, schema.clone());
assert!(res.is_ok());
let inv = batch.invert(&Arc::new(tree.clone())).unwrap();
let r = inv.apply(&mut tree, schema);
assert!(r.is_ok());
}
}