use std::{sync::Arc};
use mf_model::{node_pool::NodePool, schema::Schema, tree::Tree};
use mf_model::rpds::VectorSync;
use crate::TransformResult;
use super::step::{Step, StepResult};
#[derive(Debug, Clone)]
enum LazyDoc {
Original(Arc<NodePool>),
Pending { base: Arc<NodePool>, steps: VectorSync<Arc<dyn Step>> },
Computed(Arc<NodePool>),
}
#[derive(Debug, Clone)]
pub struct Transform {
pub base_doc: Arc<NodePool>,
lazy_doc: LazyDoc,
draft: Option<Tree>,
pub steps: VectorSync<Arc<dyn Step>>,
pub invert_steps: VectorSync<Arc<dyn Step>>,
pub schema: Arc<Schema>,
needs_recompute: bool,
}
impl Transform {
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(doc, schema), fields(
crate_name = "transform",
doc_size = doc.size()
)))]
pub fn new(
doc: Arc<NodePool>,
schema: Arc<Schema>,
) -> Transform {
Transform {
base_doc: doc.clone(),
lazy_doc: LazyDoc::Original(doc),
draft: None,
steps: VectorSync::new_sync(),
invert_steps: VectorSync::new_sync(),
schema,
needs_recompute: false,
}
}
pub fn doc(&self) -> Arc<NodePool> {
match &self.lazy_doc {
LazyDoc::Original(doc) => doc.clone(),
LazyDoc::Computed(doc) => doc.clone(),
LazyDoc::Pending { base, steps } => {
self.compute_doc_state(base.clone(), steps.clone())
},
}
}
fn get_draft(&mut self) -> TransformResult<&mut Tree> {
if self.draft.is_none() {
self.draft = Some(self.base_doc.get_inner().as_ref().clone());
}
self.draft.as_mut().ok_or_else(|| anyhow::anyhow!("草稿状态未初始化"))
}
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(self, step), fields(
crate_name = "transform",
step_count = self.steps.len()
)))]
pub fn step(
&mut self,
step: Arc<dyn Step>,
) -> TransformResult<()> {
let schema = self.schema.clone();
let draft = self.get_draft()?;
let result: StepResult = step.apply(draft, schema)?;
match result.failed {
Some(message) => Err(anyhow::anyhow!(message)),
None => {
self.add_step(step);
Ok(())
},
}
}
pub fn doc_changed(&self) -> bool {
!self.steps.is_empty()
}
fn add_step(
&mut self,
step: Arc<dyn Step>,
) {
if let Some(invert_step) = step.invert(self.base_doc.get_inner()) {
self.invert_steps.push_back_mut(invert_step);
}
self.steps.push_back_mut(step.clone());
self.lazy_doc = LazyDoc::Pending {
base: self.base_doc.clone(),
steps: self.steps.clone(),
};
self.needs_recompute = true;
}
fn compute_doc_state(
&self,
base: Arc<NodePool>,
steps: VectorSync<Arc<dyn Step>>,
) -> Arc<NodePool> {
if steps.is_empty() {
return base;
}
if let Some(ref draft) = self.draft {
NodePool::new(Arc::new(draft.clone()))
} else {
base
}
}
#[cfg_attr(feature = "dev-tracing", tracing::instrument(skip(self, steps), fields(
crate_name = "transform",
batch_size = steps.len(),
current_step_count = self.steps.len()
)))]
pub fn apply_steps_batch(
&mut self,
steps: Vec<Arc<dyn Step>>,
) -> TransformResult<()> {
let schema = self.schema.clone();
let base_doc_inner = self.base_doc.get_inner().clone();
let mut new_invert_steps = Vec::new();
for step in &steps {
if let Some(invert_step) = step.invert(&base_doc_inner) {
new_invert_steps.push(invert_step);
}
}
let draft = self.get_draft()?;
for step in &steps {
let result = step.apply(draft, schema.clone())?;
if let Some(message) = result.failed {
return Err(anyhow::anyhow!(message));
}
}
for step in steps {
self.steps.push_back_mut(step);
}
for invert_step in new_invert_steps {
self.invert_steps.push_back_mut(invert_step);
}
self.lazy_doc = LazyDoc::Pending {
base: self.base_doc.clone(),
steps: self.steps.clone(),
};
self.needs_recompute = true;
Ok(())
}
pub fn commit(&mut self) -> TransformResult<()> {
if self.needs_recompute && self.draft.is_some() {
let draft_tree = self
.draft
.as_ref()
.ok_or_else(|| anyhow::anyhow!("尝试提交时草稿状态意外丢失"))?;
let new_doc = NodePool::new(Arc::new(draft_tree.clone()));
self.base_doc = new_doc.clone();
self.lazy_doc = LazyDoc::Computed(new_doc);
self.draft = None;
self.needs_recompute = false;
}
Ok(())
}
pub fn rollback(&mut self) {
self.lazy_doc = LazyDoc::Original(self.base_doc.clone());
self.draft = None;
self.steps = VectorSync::new_sync();
self.invert_steps = VectorSync::new_sync();
self.needs_recompute = false;
}
pub fn clear_history(&mut self) {
self.steps = VectorSync::new_sync();
self.invert_steps = VectorSync::new_sync();
}
pub fn history_size(&self) -> usize {
self.steps.len()
}
}