automerge/automerge/src/storage/convert/op_as_changeop.rs
Alex Good 771733deac
Implement storage-v2
Implement parsing the binary format using the new parser library and the
new encoding types. This is superior to the previous parsing
implementation in that invalid data should never cause panics and it
exposes and interface to construct an OpSet from a saved document much
more efficiently.

Signed-off-by: Alex Good <alex@memoryandthought.me>
2022-08-22 21:16:47 +01:00

128 lines
3.2 KiB
Rust

/// Types for converting an OpTree op into a `ChangeOp` or a `DocOp`
use std::borrow::Cow;
use crate::{
convert,
op_set::OpSetMetadata,
storage::AsChangeOp,
types::{ActorId, Key, ObjId, Op, OpId, OpType, ScalarValue},
};
/// Wrap an op in an implementation of `AsChangeOp` which represents actor IDs using a reference to
/// the actor ID stored in the metadata.
///
/// Note that the methods of `AsChangeOp` will panic if the actor is missing from the metadata
pub(crate) fn op_as_actor_id<'a>(
obj: &'a ObjId,
op: &'a Op,
metadata: &'a OpSetMetadata,
) -> OpWithMetadata<'a> {
OpWithMetadata { obj, op, metadata }
}
pub(crate) struct OpWithMetadata<'a> {
obj: &'a ObjId,
op: &'a Op,
metadata: &'a OpSetMetadata,
}
impl<'a> OpWithMetadata<'a> {
fn wrap(&self, opid: &'a OpId) -> OpIdWithMetadata<'a> {
OpIdWithMetadata {
opid,
metadata: self.metadata,
}
}
}
pub(crate) struct OpIdWithMetadata<'a> {
opid: &'a OpId,
metadata: &'a OpSetMetadata,
}
impl<'a> convert::OpId<&'a ActorId> for OpIdWithMetadata<'a> {
fn counter(&self) -> u64 {
self.opid.counter()
}
fn actor(&self) -> &'a ActorId {
self.metadata.actors.get(self.opid.actor())
}
}
pub(crate) struct PredWithMetadata<'a> {
op: &'a Op,
offset: usize,
metadata: &'a OpSetMetadata,
}
impl<'a> ExactSizeIterator for PredWithMetadata<'a> {
fn len(&self) -> usize {
self.op.pred.len()
}
}
impl<'a> Iterator for PredWithMetadata<'a> {
type Item = OpIdWithMetadata<'a>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(op) = self.op.pred.get(self.offset) {
self.offset += 1;
Some(OpIdWithMetadata {
opid: op,
metadata: self.metadata,
})
} else {
None
}
}
}
impl<'a> AsChangeOp<'a> for OpWithMetadata<'a> {
type ActorId = &'a ActorId;
type OpId = OpIdWithMetadata<'a>;
type PredIter = PredWithMetadata<'a>;
fn action(&self) -> u64 {
self.op.action.action_index()
}
fn insert(&self) -> bool {
self.op.insert
}
fn val(&self) -> Cow<'a, ScalarValue> {
match &self.op.action {
OpType::Make(..) | OpType::Delete => Cow::Owned(ScalarValue::Null),
OpType::Increment(i) => Cow::Owned(ScalarValue::Int(*i)),
OpType::Put(s) => Cow::Borrowed(s),
}
}
fn obj(&self) -> convert::ObjId<Self::OpId> {
if self.obj.is_root() {
convert::ObjId::Root
} else {
convert::ObjId::Op(OpIdWithMetadata {
opid: self.obj.opid(),
metadata: self.metadata,
})
}
}
fn pred(&self) -> Self::PredIter {
PredWithMetadata {
op: self.op,
offset: 0,
metadata: self.metadata,
}
}
fn key(&self) -> convert::Key<'a, Self::OpId> {
match &self.op.key {
Key::Map(idx) => convert::Key::Prop(Cow::Owned(self.metadata.props.get(*idx).into())),
Key::Seq(e) if e.is_head() => convert::Key::Elem(convert::ElemId::Head),
Key::Seq(e) => convert::Key::Elem(convert::ElemId::Op(self.wrap(&e.0))),
}
}
}