use super::map::{Mappable, StepMap};
use crate::model::{Node, Schema};
use crate::transform::{StepError, StepResult};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AttrStep {
pub pos: usize,
pub attr: String,
pub value: serde_json::Value,
}
impl AttrStep {
pub fn get_map(&self) -> StepMap {
StepMap::EMPTY
}
pub fn map_step<M: Mappable>(&self, mapping: &M) -> Option<AttrStep> {
let pos = mapping.map_result(self.pos, 1);
if pos.deleted_after() {
None
} else {
Some(AttrStep {
pos: pos.pos,
attr: self.attr.clone(),
value: self.value.clone(),
})
}
}
pub fn invert<S: Schema>(&self, doc: &S::Node) -> super::Step<S> {
let node = doc.node_at(self.pos).unwrap_or(doc);
let attrs = node.attrs_json();
let old_value = attrs
.get(&self.attr)
.cloned()
.unwrap_or(serde_json::Value::Null);
super::Step::Attr(AttrStep {
pos: self.pos,
attr: self.attr.clone(),
value: old_value,
})
}
pub fn apply<S: Schema>(&self, doc: &S::Node) -> StepResult<S> {
let target = doc.node_at(self.pos).ok_or(StepError::NoNodeAtPosition)?;
let new_target = target.with_attr(&self.attr, self.value.clone());
let mut path: Vec<(usize, S::Node)> = Vec::new();
let mut node: &S::Node = doc;
let mut pos = self.pos;
while let Some(content) = node.content() {
let idx = match content.find_index(pos, false) {
Ok(i) => i,
Err(_) => break,
};
let child = match content.maybe_child(idx.index) {
Some(c) => c,
None => break,
};
if idx.offset == pos || child.is_text() {
path.push((idx.index, node.copy(|_| content.clone())));
break;
}
path.push((idx.index, node.copy(|_| content.clone())));
pos -= idx.offset + 1;
node = child;
}
if path.is_empty() {
return Ok(new_target);
}
let mut current = new_target;
for (child_idx, parent_copy) in path.into_iter().rev() {
let content = parent_copy.content().unwrap();
let new_content = content.replace_child(child_idx, current);
current = parent_copy.copy(|_| new_content.into_owned());
}
Ok(current)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocAttrStep {
pub attr: String,
pub value: serde_json::Value,
}
impl DocAttrStep {
pub fn get_map(&self) -> StepMap {
StepMap::EMPTY
}
pub fn map_step<M: Mappable>(&self, _mapping: &M) -> Option<DocAttrStep> {
Some(DocAttrStep {
attr: self.attr.clone(),
value: self.value.clone(),
})
}
pub fn invert<S: Schema>(&self, doc: &S::Node) -> super::Step<S> {
let attrs = doc.attrs_json();
let old_value = attrs
.get(&self.attr)
.cloned()
.unwrap_or(serde_json::Value::Null);
super::Step::DocAttr(DocAttrStep {
attr: self.attr.clone(),
value: old_value,
})
}
pub fn apply<S: Schema>(&self, doc: &S::Node) -> StepResult<S> {
Ok(doc.with_attr(&self.attr, self.value.clone()))
}
}