1use std::fmt;
6
7use serde_json::{json, Value};
8
9use crate::error::DocError;
10use crate::fragment::Fragment;
11use crate::map::{Mapping, StepMap};
12use crate::mark::Mark;
13use crate::node::Node;
14use crate::schema::Schema;
15use crate::slice::Slice;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct StepError(pub String);
20
21impl fmt::Display for StepError {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 write!(f, "step failed: {}", self.0)
24 }
25}
26impl std::error::Error for StepError {}
27
28pub trait Step: fmt::Debug + Send + Sync {
36 fn apply(&self, doc: &Node, schema: &Schema) -> Result<Node, StepError>;
38
39 fn get_map(&self) -> StepMap;
41
42 fn invert(&self, doc: &Node) -> Result<Box<dyn Step>, StepError>;
44
45 fn map(&self, mapping: &Mapping) -> Option<Box<dyn Step>>;
48
49 fn to_json(&self) -> Value;
51
52 fn clone_box(&self) -> Box<dyn Step>;
54}
55
56impl Clone for Box<dyn Step> {
57 fn clone(&self) -> Self {
58 self.clone_box()
59 }
60}
61
62fn slice_to_json(slice: &Slice) -> Value {
63 if slice.is_empty() {
64 return json!({});
65 }
66 let content: Vec<Value> = slice.content().iter().map(Node::to_json).collect();
67 json!({
68 "content": content,
69 "openStart": slice.open_start(),
70 "openEnd": slice.open_end(),
71 })
72}
73
74fn slice_from_json(schema: &Schema, v: &Value) -> Result<Slice, DocError> {
75 let obj = v
76 .as_object()
77 .ok_or_else(|| DocError::MalformedJson("slice must be an object".into()))?;
78 let content = match obj.get("content") {
79 None => return Ok(Slice::empty()),
80 Some(Value::Array(a)) => a
81 .iter()
82 .map(|n| schema.node_from_json(n))
83 .collect::<Result<Vec<_>, _>>()?,
84 Some(_) => {
85 return Err(DocError::MalformedJson(
86 "slice.content must be an array".into(),
87 ))
88 }
89 };
90 let open_start = obj.get("openStart").and_then(Value::as_u64).unwrap_or(0) as usize;
91 let open_end = obj.get("openEnd").and_then(Value::as_u64).unwrap_or(0) as usize;
92 Ok(Slice::new(
93 Fragment::from_nodes(content),
94 open_start,
95 open_end,
96 ))
97}
98
99#[derive(Debug, Clone)]
101pub struct ReplaceStep {
102 from: usize,
103 to: usize,
104 slice: Slice,
105}
106
107impl ReplaceStep {
108 pub fn new(from: usize, to: usize, slice: Slice) -> Self {
110 ReplaceStep { from, to, slice }
111 }
112}
113
114impl Step for ReplaceStep {
115 fn apply(&self, doc: &Node, schema: &Schema) -> Result<Node, StepError> {
116 doc.replace(self.from, self.to, &self.slice, schema)
117 .map_err(|e| StepError(e.to_string()))
118 }
119
120 fn get_map(&self) -> StepMap {
121 StepMap::new(vec![self.from, self.to - self.from, self.slice.size()])
122 }
123
124 fn invert(&self, doc: &Node) -> Result<Box<dyn Step>, StepError> {
125 let removed = doc
126 .slice(self.from, self.to)
127 .map_err(|e| StepError(e.to_string()))?;
128 Ok(Box::new(ReplaceStep {
129 from: self.from,
130 to: self.from + self.slice.size(),
131 slice: removed,
132 }))
133 }
134
135 fn map(&self, mapping: &Mapping) -> Option<Box<dyn Step>> {
136 let from = mapping.map_result(self.from, 1);
137 let to = mapping.map_result(self.to, -1);
138 if from.deleted_across() && to.deleted_across() {
139 return None;
140 }
141 Some(Box::new(ReplaceStep {
142 from: from.pos,
143 to: from.pos.max(to.pos),
144 slice: self.slice.clone(),
145 }))
146 }
147
148 fn to_json(&self) -> Value {
149 json!({
150 "stepType": "replace",
151 "from": self.from,
152 "to": self.to,
153 "slice": slice_to_json(&self.slice),
154 })
155 }
156
157 fn clone_box(&self) -> Box<dyn Step> {
158 Box::new(self.clone())
159 }
160}
161
162fn map_inline(frag: &Fragment, f: &dyn Fn(&Node) -> Node) -> Fragment {
163 let mut out = Vec::new();
164 for child in frag.iter() {
165 let mut c = child.clone();
166 if c.content().child_count() > 0 {
167 c = c.copy_content(map_inline(c.content(), f));
168 }
169 if c.is_inline() {
170 c = f(&c);
171 }
172 out.push(c);
173 }
174 Fragment::from_nodes(out)
175}
176
177#[derive(Debug, Clone)]
179pub struct AddMarkStep {
180 from: usize,
181 to: usize,
182 mark: Mark,
183}
184
185impl AddMarkStep {
186 pub fn new(from: usize, to: usize, mark: Mark) -> Self {
188 AddMarkStep { from, to, mark }
189 }
190}
191
192impl Step for AddMarkStep {
193 fn apply(&self, doc: &Node, schema: &Schema) -> Result<Node, StepError> {
194 let old = doc
195 .slice(self.from, self.to)
196 .map_err(|e| StepError(e.to_string()))?;
197 let mark = self.mark.clone();
198 let content = map_inline(old.content(), &|n| n.with_marks(mark.add_to_set(n.marks())));
199 let slice = Slice::new(content, old.open_start(), old.open_end());
200 doc.replace(self.from, self.to, &slice, schema)
201 .map_err(|e| StepError(e.to_string()))
202 }
203
204 fn get_map(&self) -> StepMap {
205 StepMap::identity()
206 }
207
208 fn invert(&self, _doc: &Node) -> Result<Box<dyn Step>, StepError> {
209 Ok(Box::new(RemoveMarkStep {
210 from: self.from,
211 to: self.to,
212 mark: self.mark.clone(),
213 }))
214 }
215
216 fn map(&self, mapping: &Mapping) -> Option<Box<dyn Step>> {
217 let from = mapping.map_result(self.from, 1);
218 let to = mapping.map_result(self.to, -1);
219 if (from.deleted() && to.deleted()) || from.pos >= to.pos {
220 return None;
221 }
222 Some(Box::new(AddMarkStep {
223 from: from.pos,
224 to: from.pos.max(to.pos),
225 mark: self.mark.clone(),
226 }))
227 }
228
229 fn to_json(&self) -> Value {
230 json!({
231 "stepType": "addMark",
232 "mark": self.mark.to_json(),
233 "from": self.from,
234 "to": self.to,
235 })
236 }
237
238 fn clone_box(&self) -> Box<dyn Step> {
239 Box::new(self.clone())
240 }
241}
242
243#[derive(Debug, Clone)]
245pub struct RemoveMarkStep {
246 from: usize,
247 to: usize,
248 mark: Mark,
249}
250
251impl RemoveMarkStep {
252 pub fn new(from: usize, to: usize, mark: Mark) -> Self {
254 RemoveMarkStep { from, to, mark }
255 }
256}
257
258impl Step for RemoveMarkStep {
259 fn apply(&self, doc: &Node, schema: &Schema) -> Result<Node, StepError> {
260 let old = doc
261 .slice(self.from, self.to)
262 .map_err(|e| StepError(e.to_string()))?;
263 let mark = self.mark.clone();
264 let content = map_inline(old.content(), &|n| {
265 n.with_marks(mark.remove_from_set(n.marks()))
266 });
267 let slice = Slice::new(content, old.open_start(), old.open_end());
268 doc.replace(self.from, self.to, &slice, schema)
269 .map_err(|e| StepError(e.to_string()))
270 }
271
272 fn get_map(&self) -> StepMap {
273 StepMap::identity()
274 }
275
276 fn invert(&self, _doc: &Node) -> Result<Box<dyn Step>, StepError> {
277 Ok(Box::new(AddMarkStep {
278 from: self.from,
279 to: self.to,
280 mark: self.mark.clone(),
281 }))
282 }
283
284 fn map(&self, mapping: &Mapping) -> Option<Box<dyn Step>> {
285 let from = mapping.map_result(self.from, 1);
286 let to = mapping.map_result(self.to, -1);
287 if (from.deleted() && to.deleted()) || from.pos >= to.pos {
288 return None;
289 }
290 Some(Box::new(RemoveMarkStep {
291 from: from.pos,
292 to: from.pos.max(to.pos),
293 mark: self.mark.clone(),
294 }))
295 }
296
297 fn to_json(&self) -> Value {
298 json!({
299 "stepType": "removeMark",
300 "mark": self.mark.to_json(),
301 "from": self.from,
302 "to": self.to,
303 })
304 }
305
306 fn clone_box(&self) -> Box<dyn Step> {
307 Box::new(self.clone())
308 }
309}
310
311fn rebuild_at(node: &Node, pos: usize, f: &dyn Fn(&Node) -> Node) -> Option<Node> {
312 let (index, offset) = node.content().find_index(pos);
313 let child = node.content().children().get(index)?;
314 if offset == pos {
315 let new_child = f(child);
316 return Some(node.copy_content(node.content().replace_child(index, new_child)));
317 }
318 let inner = rebuild_at(child, pos - offset - 1, f)?;
319 Some(node.copy_content(node.content().replace_child(index, inner)))
320}
321
322#[derive(Debug, Clone)]
324pub struct AttrStep {
325 pos: usize,
326 attr: String,
327 value: Value,
328}
329
330impl AttrStep {
331 pub fn new(pos: usize, attr: &str, value: Value) -> Self {
333 AttrStep {
334 pos,
335 attr: attr.to_string(),
336 value,
337 }
338 }
339}
340
341impl Step for AttrStep {
342 fn apply(&self, doc: &Node, _schema: &Schema) -> Result<Node, StepError> {
343 let attr = self.attr.clone();
344 let value = self.value.clone();
345 rebuild_at(doc, self.pos, &|n| {
346 let mut attrs = n.attrs().clone();
347 attrs.insert(attr.clone(), value.clone());
348 n.with_attrs(attrs)
349 })
350 .ok_or_else(|| StepError("no node at the attribute step's position".into()))
351 }
352
353 fn get_map(&self) -> StepMap {
354 StepMap::identity()
355 }
356
357 fn invert(&self, doc: &Node) -> Result<Box<dyn Step>, StepError> {
358 let node = doc
359 .node_at(self.pos)
360 .ok_or_else(|| StepError("no node at the attribute step's position".into()))?;
361 let old = node.attrs().get(&self.attr).cloned().unwrap_or(Value::Null);
362 Ok(Box::new(AttrStep {
363 pos: self.pos,
364 attr: self.attr.clone(),
365 value: old,
366 }))
367 }
368
369 fn map(&self, mapping: &Mapping) -> Option<Box<dyn Step>> {
370 let r = mapping.map_result(self.pos, 1);
371 if r.deleted_after() {
372 None
373 } else {
374 Some(Box::new(AttrStep {
375 pos: r.pos,
376 attr: self.attr.clone(),
377 value: self.value.clone(),
378 }))
379 }
380 }
381
382 fn to_json(&self) -> Value {
383 json!({
384 "stepType": "attr",
385 "pos": self.pos,
386 "attr": self.attr,
387 "value": self.value,
388 })
389 }
390
391 fn clone_box(&self) -> Box<dyn Step> {
392 Box::new(self.clone())
393 }
394}
395
396#[derive(Debug, Clone)]
400pub struct ReplaceAroundStep {
401 from: usize,
402 to: usize,
403 gap_from: usize,
404 gap_to: usize,
405 slice: Slice,
406 insert: usize,
407}
408
409impl ReplaceAroundStep {
410 pub fn new(
412 from: usize,
413 to: usize,
414 gap_from: usize,
415 gap_to: usize,
416 slice: Slice,
417 insert: usize,
418 ) -> Self {
419 ReplaceAroundStep {
420 from,
421 to,
422 gap_from,
423 gap_to,
424 slice,
425 insert,
426 }
427 }
428}
429
430impl Step for ReplaceAroundStep {
431 fn apply(&self, doc: &Node, schema: &Schema) -> Result<Node, StepError> {
432 let gap = doc
433 .slice(self.gap_from, self.gap_to)
434 .map_err(|e| StepError(e.to_string()))?;
435 if gap.open_start() > 0 || gap.open_end() > 0 {
436 return Err(StepError("structure gap can't be inserted".into()));
437 }
438 let inserted = self
439 .slice
440 .insert_at(self.insert, gap.content().clone())
441 .ok_or_else(|| StepError("content does not fit in gap".into()))?;
442 doc.replace(self.from, self.to, &inserted, schema)
443 .map_err(|e| StepError(e.to_string()))
444 }
445
446 fn get_map(&self) -> StepMap {
447 StepMap::new(vec![
448 self.from,
449 self.gap_from - self.from,
450 self.insert,
451 self.gap_to,
452 self.to - self.gap_to,
453 self.slice.size().saturating_sub(self.insert),
454 ])
455 }
456
457 fn invert(&self, doc: &Node) -> Result<Box<dyn Step>, StepError> {
458 let gap = self.gap_to - self.gap_from;
459 let removed = doc
460 .slice(self.from, self.to)
461 .map_err(|e| StepError(e.to_string()))?
462 .remove_between(self.gap_from - self.from, self.gap_to - self.from)
463 .ok_or_else(|| StepError("cannot invert replace-around".into()))?;
464 Ok(Box::new(ReplaceAroundStep {
465 from: self.from,
466 to: self.from + self.slice.size() + gap,
467 gap_from: self.from + self.insert,
468 gap_to: self.from + self.insert + gap,
469 slice: removed,
470 insert: self.gap_from - self.from,
471 }))
472 }
473
474 fn map(&self, mapping: &Mapping) -> Option<Box<dyn Step>> {
475 let from = mapping.map_result(self.from, 1);
476 let to = mapping.map_result(self.to, -1);
477 let gap_from = if self.from == self.gap_from {
478 from.pos
479 } else {
480 mapping.map(self.gap_from, -1)
481 };
482 let gap_to = if self.to == self.gap_to {
483 to.pos
484 } else {
485 mapping.map(self.gap_to, 1)
486 };
487 if (from.deleted_across() && to.deleted_across()) || gap_from < from.pos || gap_to > to.pos
488 {
489 return None;
490 }
491 Some(Box::new(ReplaceAroundStep {
492 from: from.pos,
493 to: to.pos,
494 gap_from,
495 gap_to,
496 slice: self.slice.clone(),
497 insert: self.insert,
498 }))
499 }
500
501 fn to_json(&self) -> Value {
502 json!({
503 "stepType": "replaceAround",
504 "from": self.from,
505 "to": self.to,
506 "gapFrom": self.gap_from,
507 "gapTo": self.gap_to,
508 "insert": self.insert,
509 "slice": slice_to_json(&self.slice),
510 })
511 }
512
513 fn clone_box(&self) -> Box<dyn Step> {
514 Box::new(self.clone())
515 }
516}
517
518pub fn step_from_json(schema: &Schema, v: &Value) -> Result<Box<dyn Step>, DocError> {
520 let obj = v
521 .as_object()
522 .ok_or_else(|| DocError::MalformedJson("step must be an object".into()))?;
523 let kind = obj
524 .get("stepType")
525 .and_then(Value::as_str)
526 .ok_or_else(|| DocError::MalformedJson("step missing `stepType`".into()))?;
527 match kind {
528 "replace" => {
529 let from = obj
530 .get("from")
531 .and_then(Value::as_u64)
532 .ok_or_else(|| DocError::MalformedJson("replace step missing `from`".into()))?
533 as usize;
534 let to = obj
535 .get("to")
536 .and_then(Value::as_u64)
537 .ok_or_else(|| DocError::MalformedJson("replace step missing `to`".into()))?
538 as usize;
539 let slice = match obj.get("slice") {
540 Some(s) => slice_from_json(schema, s)?,
541 None => Slice::empty(),
542 };
543 Ok(Box::new(ReplaceStep::new(from, to, slice)))
544 }
545 "addMark" | "removeMark" => {
546 let from = obj
547 .get("from")
548 .and_then(Value::as_u64)
549 .ok_or_else(|| DocError::MalformedJson("mark step missing `from`".into()))?
550 as usize;
551 let to = obj
552 .get("to")
553 .and_then(Value::as_u64)
554 .ok_or_else(|| DocError::MalformedJson("mark step missing `to`".into()))?
555 as usize;
556 let mark = schema.mark_from_json(
557 obj.get("mark")
558 .ok_or_else(|| DocError::MalformedJson("mark step missing `mark`".into()))?,
559 )?;
560 Ok(if kind == "addMark" {
561 Box::new(AddMarkStep::new(from, to, mark))
562 } else {
563 Box::new(RemoveMarkStep::new(from, to, mark))
564 })
565 }
566 "replaceAround" => {
567 let g = |k: &str| {
568 obj.get(k)
569 .and_then(Value::as_u64)
570 .map(|n| n as usize)
571 .ok_or_else(|| {
572 DocError::MalformedJson(format!("replaceAround step missing `{k}`"))
573 })
574 };
575 let slice = match obj.get("slice") {
576 Some(s) => slice_from_json(schema, s)?,
577 None => Slice::empty(),
578 };
579 Ok(Box::new(ReplaceAroundStep::new(
580 g("from")?,
581 g("to")?,
582 g("gapFrom")?,
583 g("gapTo")?,
584 slice,
585 g("insert")?,
586 )))
587 }
588 "attr" => {
589 let pos = obj
590 .get("pos")
591 .and_then(Value::as_u64)
592 .ok_or_else(|| DocError::MalformedJson("attr step missing `pos`".into()))?
593 as usize;
594 let attr = obj
595 .get("attr")
596 .and_then(Value::as_str)
597 .ok_or_else(|| DocError::MalformedJson("attr step missing `attr`".into()))?;
598 let value = obj.get("value").cloned().unwrap_or(Value::Null);
599 Ok(Box::new(AttrStep::new(pos, attr, value)))
600 }
601 other => Err(DocError::MalformedJson(format!(
602 "unknown stepType `{other}`"
603 ))),
604 }
605}