aboutsummaryrefslogtreecommitdiffstats
path: root/src/document/inspect.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/document/inspect.rs')
-rw-r--r--src/document/inspect.rs97
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() {