diff options
Diffstat (limited to 'src/document')
| -rw-r--r-- | src/document/inspect.rs | 97 |
1 files changed, 89 insertions, 8 deletions
diff --git a/src/document/inspect.rs b/src/document/inspect.rs index b39386e..d395008 100644 --- a/src/document/inspect.rs +++ b/src/document/inspect.rs @@ -11,9 +11,6 @@ //! change lacks a dependency, and can't be applied anyway) is placed at the root, which //! is within every path's reach. -// Judging changes on receipt is what uses it; until then, the benchmark and tests. -#![cfg_attr(not(test), allow(dead_code))] - use std::collections::{BTreeSet, HashMap}; use automerge::legacy::{Key, ObjectId, OpId, OpType}; @@ -24,6 +21,31 @@ use super::{Document, ReadDoc}; /// A store path, by segments. pub(crate) type Path = Vec<String>; +/// An operation, as rules see it (`Inspector::ops`): where, and what it does there. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct Op { + /// What it touches. + pub(crate) path: Path, + pub(crate) action: Action, +} + +/// What an operation does at its path. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum Action { + /// Makes a map there. + MakeMap, + /// Puts a string there. + PutString(String), + /// Deletes what is there. + Delete, + /// Changes what is there, a list or text it is in (`Place::in_list`). + Within, + /// In an object nowhere to be found (`Inspector::known`). + Nowhere, + /// Anything else there: makes another object, puts another scalar, increments. + Other, +} + /// Where an object is: its path, cut at the first list or text on the way /// (`in_list`: it is inside one, or is one). #[derive(Debug, Clone, PartialEq, Eq)] @@ -79,10 +101,20 @@ impl<'a> Inspector<'a> { } } - /// The paths each of `changes` touches, in their order. The objects they make are - /// placed first, whatever their order, so a change is judged the same however the - /// changes came. + /// The paths each of `changes` touches, in their order (`ops`): for the benchmark + /// and tests. + #[cfg_attr(not(test), allow(dead_code))] pub(crate) fn paths(&mut self, changes: &[ExpandedChange]) -> Vec<BTreeSet<Path>> { + self.ops(changes) + .into_iter() + .map(|ops| ops.into_iter().map(|op| op.path).collect()) + .collect() + } + + /// The operations of each of `changes`, in their order, as rules see them. The + /// objects they make are placed first, whatever their order, so a change is judged + /// the same however the changes came. + pub(crate) fn ops(&mut self, changes: &[ExpandedChange]) -> Vec<Vec<Op>> { // The objects made, by the operation that made them: placed as their parents // are, until no more can be (a parent made later in the list, placed in a later // round). @@ -119,9 +151,23 @@ impl<'a> Inspector<'a> { .operations .iter() .map(|op| match self.known(&op.obj) { - Some(place) => place.touched(&op.key), + Some(place) => Op { + path: place.touched(&op.key), + action: match &op.action { + _ if place.in_list || op.insert => Action::Within, + OpType::Make(automerge::ObjType::Map) => Action::MakeMap, + OpType::Put(automerge::ScalarValue::Str(text)) => { + Action::PutString(text.to_string()) + } + OpType::Delete => Action::Delete, + _ => Action::Other, + }, + }, // Nowhere to be found: within every path's reach. - None => Vec::new(), + None => Op { + path: Vec::new(), + action: Action::Nowhere, + }, }) .collect() }) @@ -214,6 +260,41 @@ mod tests { assert_eq!(backwards, all); } + /// What each operation does where: a map made, a string put, a deletion, what is + /// inside a list, anything else. + #[test] + fn what_operations_do() { + let mut doc = Document::new(); + change(&mut doc, |doc| { + let config = doc.put_object(ROOT, "config", ObjType::Map).unwrap(); + doc.put(&config, "mode", "fast").unwrap(); + doc.put(&config, "level", 3).unwrap(); + doc.delete(&config, "mode").unwrap(); + let queue = doc.put_object(ROOT, "queue", ObjType::List).unwrap(); + doc.insert(&queue, 0, "job").unwrap(); + }); + let empty = Document::new(); + let ops = Inspector::new(&empty).ops(&decoded(&doc, &[])); + let actions: Vec<(Path, Action)> = ops[0] + .iter() + .map(|op| (op.path.clone(), op.action.clone())) + .collect(); + assert_eq!( + actions, + vec![ + (path(&["config"]), Action::MakeMap), + ( + path(&["config", "mode"]), + Action::PutString("fast".to_owned()) + ), + (path(&["config", "level"]), Action::Other), + (path(&["config", "mode"]), Action::Delete), + (path(&["queue"]), Action::Other), + (path(&["queue"]), Action::Within), + ] + ); + } + /// Inside a list or text, a path stops at it: items move. #[test] fn lists_and_text_stop_paths() { |
