use super::{
ListDiff, ListDiffItem, ListRange, ListRangeItem, MapDiff, MapDiffItem, MapRange, MapRangeItem,
Span, SpanDiff, SpanInternal, SpansDiff, SpansInternal,
};
use crate::clock::{Clock, ClockRange};
use crate::exid::ExId;
use crate::op_set2::op_set::{ObjIdIter, OpSet};
use crate::op_set2::types::ValueRef;
use crate::patches::PatchLog;
use crate::types::{ObjId, ObjMeta, ObjType, Prop};
use crate::Automerge;
use crate::TextEncoding;
use std::collections::BTreeMap;
use std::ops::Range;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct DocIter<'a> {
op_set: Option<&'a OpSet>,
obj_export: Arc<ExId>,
inner: DocIterInternal<'a>,
}
impl<'a> DocIter<'a> {
pub(crate) fn empty(encoding: TextEncoding) -> Self {
Self {
op_set: None,
obj_export: Arc::new(ExId::Root),
inner: DocIterInternal::empty(encoding),
}
}
fn encoding(&self) -> TextEncoding {
self.inner.span_iter.encoding()
}
fn clock(&self) -> Option<&Clock> {
self.inner.span_iter.clock()
}
pub(crate) fn new(doc: &'a Automerge, obj: ObjMeta, clock: Option<Clock>) -> Self {
let obj_export = Arc::new(doc.ops().id_to_exid(obj.id.0));
let op_set = Some(doc.ops());
Self {
op_set,
obj_export,
inner: DocIterInternal::new(doc, obj, clock),
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct DocIterInternal<'a> {
next_objs: BTreeMap<ObjId, IterType>,
pub(crate) path_map: BTreeMap<ObjId, (Prop, ObjId)>,
obj_id_iter: ObjIdIter<'a>,
map_iter: MapRange<'a>,
list_iter: ListRange<'a>,
span_iter: SpansInternal<'a>,
iter_type: IterType,
obj: ObjId,
}
#[derive(Debug, Clone)]
pub(crate) struct DiffIter<'a> {
next_objs: BTreeMap<ObjId, IterType>,
path_map: BTreeMap<ObjId, (Prop, ObjId)>,
obj_id_iter: ObjIdIter<'a>,
map_iter: MapDiff<'a>,
list_iter: ListDiff<'a>,
span_iter: SpansDiff<'a>,
iter_type: IterType,
recursive: bool,
obj: ObjId,
}
impl<'a> DiffIter<'a> {
pub(crate) fn log(
doc: &'a Automerge,
obj: ObjMeta,
clock: ClockRange,
log: &mut PatchLog,
recursive: bool,
) -> BTreeMap<ObjId, (Prop, ObjId)> {
let encoding = doc.text_encoding();
let mut iter = DiffIter::new(doc, obj, clock, recursive);
for item in iter.by_ref() {
item.log(log, encoding);
}
iter.path_map
}
pub(crate) fn new(
doc: &'a Automerge,
obj: ObjMeta,
clock: ClockRange,
recursive: bool,
) -> Self {
let op_set = doc.ops();
let iter_type = IterType::new(obj.typ);
let obj = obj.id;
let mut obj_id_iter = op_set.obj_id_iter();
let scope = obj_id_iter.seek_to_value(obj);
let map_iter = MapDiff::new(op_set, scope.clone(), clock.clone());
let list_iter = ListDiff::new(op_set, scope.clone(), clock.clone());
let span_iter = SpansDiff::new(op_set, scope, clock, doc.text_encoding());
let path_map = BTreeMap::new();
let next_objs = BTreeMap::new();
DiffIter {
map_iter,
list_iter,
span_iter,
obj,
iter_type,
obj_id_iter,
next_objs,
path_map,
recursive,
}
}
fn process_item(&mut self, item: DocDiffItem<'a>) -> Option<DocObjDiffItem<'a>> {
if let Some((next_obj, next_typ)) = item.make_obj() {
let prop = item.prop();
if self.recursive {
self.next_objs.insert(next_obj, next_typ);
}
self.path_map.insert(next_obj, (prop, self.obj));
}
Some(DocObjDiffItem {
obj: self.obj,
item,
})
}
fn shift(&mut self, next_type: IterType, next_range: Range<usize>) -> Option<DocDiffItem<'a>> {
match next_type {
IterType::Map => Some(DocDiffItem::Map(self.map_iter.shift_next(next_range)?)),
IterType::List => Some(DocDiffItem::List(self.list_iter.shift_next(next_range)?)),
IterType::Text => Some(DocDiffItem::Text(self.span_iter.shift_next(next_range)?)),
}
}
fn next_object(&mut self) -> Option<Option<DocObjDiffItem<'a>>> {
let (next, next_type) = self.next_objs.pop_first()?;
let next_range = self.obj_id_iter.seek_to_value(next);
if next_range.is_empty() {
Some(None)
} else if let Some(item) = self.shift(next_type, next_range) {
self.obj = next;
self.iter_type = next_type;
Some(self.process_item(item))
} else {
Some(None)
}
}
fn next_prop(&mut self) -> Option<DocObjDiffItem<'a>> {
match self.iter_type {
IterType::Map => {
let map = DocDiffItem::Map(self.map_iter.next()?);
self.process_item(map)
}
IterType::List => {
let list = DocDiffItem::List(self.list_iter.next()?);
self.process_item(list)
}
IterType::Text => {
let span = DocDiffItem::Text(self.span_iter.next()?);
self.process_item(span)
}
}
}
}
impl<'a> DocIterInternal<'a> {
fn new(doc: &'a Automerge, obj: ObjMeta, clock: Option<Clock>) -> Self {
let op_set = doc.ops();
let iter_type = IterType::new(obj.typ);
let obj = obj.id;
let mut obj_id_iter = op_set.obj_id_iter();
let scope = obj_id_iter.seek_to_value(obj);
let map_iter = MapRange::new(op_set, scope.clone(), clock.clone());
let list_iter = ListRange::new(op_set, scope.clone(), clock.clone(), ..);
let span_iter = SpansInternal::new(op_set, scope, clock, doc.text_encoding());
let path_map = BTreeMap::new();
let next_objs = BTreeMap::new();
DocIterInternal {
map_iter,
list_iter,
span_iter,
obj,
iter_type,
obj_id_iter,
next_objs,
path_map,
}
}
fn empty(encoding: TextEncoding) -> Self {
Self {
next_objs: BTreeMap::default(),
path_map: BTreeMap::default(),
obj_id_iter: ObjIdIter::default(),
map_iter: MapRange::default(),
list_iter: ListRange::default(),
span_iter: SpansInternal::empty(encoding),
iter_type: IterType::Map,
obj: ObjId::root(),
}
}
fn process_item(&mut self, item: DocItemInternal<'a>) -> Option<DocObjItemInternal<'a>> {
if let Some((next_obj, next_typ)) = item.make_obj() {
let prop = item.prop();
self.next_objs.insert(next_obj, next_typ);
self.path_map.insert(next_obj, (prop, self.obj));
}
Some(DocObjItemInternal {
obj: self.obj,
item,
})
}
fn shift(
&mut self,
next_type: IterType,
next_range: Range<usize>,
) -> Option<DocItemInternal<'a>> {
match next_type {
IterType::Map => Some(DocItemInternal::Map(self.map_iter.shift_next(next_range)?)),
IterType::List => Some(DocItemInternal::List(
self.list_iter.shift_next(next_range)?,
)),
IterType::Text => Some(DocItemInternal::Text(
self.span_iter.shift_next(next_range)?,
)),
}
}
fn next_object(&mut self) -> Option<Option<DocObjItemInternal<'a>>> {
let (next, next_type) = self.next_objs.pop_first()?;
let next_range = self.obj_id_iter.seek_to_value(next);
if next_range.is_empty() {
Some(None)
} else if let Some(item) = self.shift(next_type, next_range) {
self.obj = next;
self.iter_type = next_type;
Some(self.process_item(item))
} else {
Some(None)
}
}
fn next_prop(&mut self) -> Option<DocObjItemInternal<'a>> {
match self.iter_type {
IterType::Map => {
let map = DocItemInternal::Map(self.map_iter.next()?);
self.process_item(map)
}
IterType::List => {
let list = DocItemInternal::List(self.list_iter.next()?);
self.process_item(list)
}
IterType::Text => {
let span = DocItemInternal::Text(self.span_iter.next()?);
self.process_item(span)
}
}
}
}
#[derive(Debug, Copy, Clone)]
enum IterType {
Map,
List,
Text,
}
impl IterType {
fn new(obj_type: ObjType) -> Self {
match obj_type {
ObjType::Text => IterType::Text,
ObjType::Map => IterType::Map,
_ => IterType::List,
}
}
}
impl<'a> Iterator for DocIter<'a> {
type Item = DocObjItem<'a>;
fn next(&mut self) -> Option<Self::Item> {
let DocObjItemInternal { obj, item } = self.inner.next()?;
if *self.obj_export != obj {
self.obj_export = Arc::new(self.op_set?.id_to_exid(self.inner.obj.0));
}
Some(DocObjItem {
obj: self.obj_export.clone(),
item: item.export(self.op_set?, self.clock(), self.encoding()),
})
}
}
impl<'a> Iterator for DiffIter<'a> {
type Item = DocObjDiffItem<'a>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(item) = self.next_prop() {
return Some(item);
}
loop {
if let Some(item) = self.next_object()? {
return Some(item);
}
}
}
}
impl<'a> Iterator for DocIterInternal<'a> {
type Item = DocObjItemInternal<'a>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(item) = self.next_prop() {
return Some(item);
}
loop {
if let Some(item) = self.next_object()? {
return Some(item);
}
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DocObjItem<'a> {
pub obj: Arc<ExId>,
pub item: DocItem<'a>,
}
#[derive(Debug, Clone)]
pub(crate) struct DocObjItemInternal<'a> {
pub(crate) obj: ObjId,
pub(crate) item: DocItemInternal<'a>,
}
#[derive(Debug, Clone)]
pub(crate) struct DocObjDiffItem<'a> {
pub(crate) obj: ObjId,
pub(crate) item: DocDiffItem<'a>,
}
impl DocObjDiffItem<'_> {
pub(crate) fn log(self, log: &mut PatchLog, encoding: TextEncoding) {
match self.item {
DocDiffItem::Map(m) => m.log(self.obj, log, encoding),
DocDiffItem::List(l) => l.log(self.obj, log, encoding),
DocDiffItem::Text(t) => t.log(self.obj, log, encoding),
}
}
}
impl<'a> DocObjItem<'a> {
pub fn key(&self) -> Option<&str> {
if let DocItem::Map(MapRangeItem { key, .. }) = &self.item {
Some(key)
} else {
None
}
}
pub fn value(&self) -> Option<ValueRef<'a>> {
match &self.item {
DocItem::Map(MapRangeItem { value, .. }) => Some(value.clone()),
DocItem::List(ListRangeItem { value, .. }) => Some(value.clone()),
DocItem::Text(span) => Some(ValueRef::str(span.as_str()).into_owned()),
}
}
}
#[derive(PartialEq, Debug, Clone)]
pub enum DocItem<'a> {
Map(MapRangeItem<'a>),
List(ListRangeItem<'a>),
Text(Span),
}
#[derive(Debug, Clone)]
pub(crate) enum DocItemInternal<'a> {
Map(MapRangeItem<'a>),
List(ListRangeItem<'a>),
Text(SpanInternal),
}
#[derive(Debug, Clone)]
pub(crate) enum DocDiffItem<'a> {
Map(MapDiffItem<'a>),
List(ListDiffItem<'a>),
Text(SpanDiff),
}
impl DocDiffItem<'_> {
fn prop(&self) -> Prop {
match self {
Self::Map(m) => Prop::Map(m.key.to_string()),
Self::List(l) => Prop::Seq(l.index),
Self::Text(SpanDiff {
span: SpanInternal::Obj(_, index, _),
..
}) => Prop::Seq(*index),
Self::Text(SpanDiff {
span: SpanInternal::Text(_, index, _),
..
}) => Prop::Seq(*index),
}
}
fn make_obj(&self) -> Option<(ObjId, IterType)> {
match self {
Self::Map(MapDiffItem {
value: ValueRef::Object(ot),
diff,
id,
..
}) if diff.is_visible() => {
let new_typ = IterType::new(*ot);
let new_obj = ObjId(*id);
Some((new_obj, new_typ))
}
Self::List(ListDiffItem {
value: ValueRef::Object(ot),
diff,
id,
..
}) if diff.is_visible() => {
let new_typ = IterType::new(*ot);
let new_obj = ObjId(*id);
Some((new_obj, new_typ))
}
Self::Text(SpanDiff {
span: SpanInternal::Obj(id, _, _),
diff,
}) if diff.is_visible() => {
let new_typ = IterType::new(ObjType::Map);
let new_obj = ObjId(*id);
Some((new_obj, new_typ))
}
_ => None,
}
}
}
impl<'a> DocItemInternal<'a> {
fn prop(&self) -> Prop {
match self {
Self::Map(m) => Prop::Map(m.key.to_string()),
Self::List(l) => Prop::Seq(l.index),
Self::Text(SpanInternal::Obj(_, index, _)) => Prop::Seq(*index),
Self::Text(SpanInternal::Text(_, index, _)) => Prop::Seq(*index),
}
}
fn export(
self,
op_set: &'a OpSet,
clock: Option<&Clock>,
encoding: TextEncoding,
) -> DocItem<'a> {
match self {
Self::Map(m) => DocItem::Map(m),
Self::List(l) => DocItem::List(l),
Self::Text(t) => DocItem::Text(t.export(op_set, clock, encoding)),
}
}
fn make_obj(&self) -> Option<(ObjId, IterType)> {
match self {
DocItemInternal::Map(MapRangeItem {
value: ValueRef::Object(ot),
maybe_exid,
..
}) => {
let new_typ = IterType::new(*ot);
let new_obj = ObjId(maybe_exid.id);
Some((new_obj, new_typ))
}
DocItemInternal::List(ListRangeItem {
value: ValueRef::Object(ot),
maybe_exid,
..
}) => {
let new_typ = IterType::new(*ot);
let new_obj = ObjId(maybe_exid.id);
Some((new_obj, new_typ))
}
DocItemInternal::Text(SpanInternal::Obj(id, _, _)) => {
let new_typ = IterType::new(ObjType::Map);
let new_obj = ObjId(*id);
Some((new_obj, new_typ))
}
_ => None,
}
}
}
impl<'a> DocItem<'a> {
pub fn id(&self) -> Option<ExId> {
match self {
DocItem::Map(m) => Some(m.id()),
DocItem::List(l) => Some(l.id()),
_ => None,
}
}
pub fn value(&self) -> Option<&ValueRef<'a>> {
match self {
DocItem::Map(MapRangeItem { value, .. }) => Some(value),
DocItem::List(ListRangeItem { value, .. }) => Some(value),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::transaction::Transactable;
use crate::{Automerge, ObjType, ReadDoc, ROOT};
#[test]
fn doc_iter() {
let mut doc = Automerge::new();
let mut tx = doc.transaction();
tx.put(&ROOT, "key01", "value1").unwrap();
tx.put(&ROOT, "key02", "value2").unwrap();
let map1 = tx.put_object(&ROOT, "key03", ObjType::Map).unwrap();
tx.put(&ROOT, "key04", "value4").unwrap();
let _map2 = tx.put_object(&ROOT, "key05", ObjType::Map).unwrap();
let map3 = tx.put_object(&ROOT, "key06", ObjType::Map).unwrap(); let map4 = tx.put_object(&ROOT, "key07", ObjType::Map).unwrap();
let list1 = tx.put_object(&ROOT, "key08", ObjType::List).unwrap();
let _map5 = tx.put_object(&ROOT, "key09", ObjType::Map).unwrap();
let map6 = tx.put_object(&ROOT, "key10", ObjType::Map).unwrap();
let list2 = tx.put_object(&ROOT, "key11", ObjType::List).unwrap();
let _map7 = tx.put_object(&ROOT, "key12", ObjType::Map).unwrap();
let text1 = tx.put_object(&ROOT, "key13", ObjType::Text).unwrap();
tx.splice_text(&text1, 0, 0, "hello world").unwrap();
tx.put(&map1, "m1key1", "m1value1").unwrap();
tx.put(&map3, "m3key1", "m3value1").unwrap();
tx.put(&map3, "m3key2", "m3value2").unwrap();
tx.put(&map3, "m3key3", "m3value3").unwrap();
tx.put(&map4, "m4key1", "m4value1").unwrap();
tx.put(&map4, "m4key2", "m4value2").unwrap();
tx.insert(&list1, 0, "l1e1").unwrap();
tx.insert(&list1, 1, "l1e2").unwrap();
tx.insert(&list1, 2, "l1e3").unwrap();
tx.put(&map6, "m6key1", "m6value1").unwrap();
tx.put(&map6, "m6key2", "m6value2").unwrap();
tx.put(&map6, "m6key3", "m6value3").unwrap();
tx.insert(&list2, 0, "l2e1").unwrap();
tx.delete(&ROOT, "key06").unwrap();
tx.delete(&map6, "m6key2").unwrap();
tx.commit();
doc.dump();
let props: Vec<_> = doc.iter().collect();
let values: Vec<_> = props
.iter()
.map(|p| (p.key().unwrap_or(""), p.value().unwrap()))
.collect();
let answers = vec![
("key01", "value1".into()),
("key02", "value2".into()),
("key03", ValueRef::Object(ObjType::Map)),
("key04", "value4".into()),
("key05", ValueRef::Object(ObjType::Map)),
("key07", ValueRef::Object(ObjType::Map)),
("key08", ValueRef::Object(ObjType::List)),
("key09", ValueRef::Object(ObjType::Map)),
("key10", ValueRef::Object(ObjType::Map)),
("key11", ValueRef::Object(ObjType::List)),
("key12", ValueRef::Object(ObjType::Map)),
("key13", ValueRef::Object(ObjType::Text)),
("m1key1", "m1value1".into()),
("m4key1", "m4value1".into()),
("m4key2", "m4value2".into()),
("", "l1e1".into()),
("", "l1e2".into()),
("", "l1e3".into()),
("m6key1", "m6value1".into()),
("m6key3", "m6value3".into()),
("", "l2e1".into()),
("", "hello world".into()),
];
let max = std::cmp::max(answers.len(), values.len());
for i in 0..max {
assert_eq!(values.get(i), answers.get(i));
}
}
}