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>
145 lines
3.5 KiB
Rust
145 lines
3.5 KiB
Rust
use std::borrow::Cow;
|
|
|
|
use crate::{
|
|
convert,
|
|
indexed_cache::IndexedCache,
|
|
storage::AsDocOp,
|
|
types::{ElemId, Key, ObjId, Op, OpId, OpType, ScalarValue},
|
|
};
|
|
|
|
/// Create an [`AsDocOp`] implementation for a [`crate::types::Op`]
|
|
///
|
|
/// # Arguments
|
|
/// * actors - A vector where the i'th element is the actor index of the document encoding of actor
|
|
/// i, as returned by [`OpSetMetadata.actors.encode_index`]
|
|
/// * props - An indexed cache containing the properties in this op_as_docop
|
|
/// * obj - The object ID this op refers too
|
|
/// * op - The op itself
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// The methods of the resulting `AsDocOp` implementation will panic if any actor ID in the op
|
|
/// references an index not in `actors` or a property not in `props`
|
|
pub(crate) fn op_as_docop<'a>(
|
|
actors: &'a [usize],
|
|
props: &'a IndexedCache<String>,
|
|
obj: &'a ObjId,
|
|
op: &'a Op,
|
|
) -> OpAsDocOp<'a> {
|
|
OpAsDocOp {
|
|
op,
|
|
obj,
|
|
actor_lookup: actors,
|
|
props,
|
|
}
|
|
}
|
|
|
|
pub(crate) struct OpAsDocOp<'a> {
|
|
op: &'a Op,
|
|
obj: &'a ObjId,
|
|
actor_lookup: &'a [usize],
|
|
props: &'a IndexedCache<String>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct DocOpId {
|
|
actor: usize,
|
|
counter: u64,
|
|
}
|
|
|
|
impl convert::OpId<usize> for DocOpId {
|
|
fn actor(&self) -> usize {
|
|
self.actor
|
|
}
|
|
|
|
fn counter(&self) -> u64 {
|
|
self.counter
|
|
}
|
|
}
|
|
|
|
impl<'a> OpAsDocOp<'a> {}
|
|
|
|
impl<'a> AsDocOp<'a> for OpAsDocOp<'a> {
|
|
type ActorId = usize;
|
|
type OpId = DocOpId;
|
|
type SuccIter = OpAsDocOpSuccIter<'a>;
|
|
|
|
fn id(&self) -> Self::OpId {
|
|
translate(self.actor_lookup, &self.op.id)
|
|
}
|
|
|
|
fn obj(&self) -> convert::ObjId<Self::OpId> {
|
|
if self.obj.is_root() {
|
|
convert::ObjId::Root
|
|
} else {
|
|
convert::ObjId::Op(translate(self.actor_lookup, self.obj.opid()))
|
|
}
|
|
}
|
|
|
|
fn key(&self) -> convert::Key<'a, Self::OpId> {
|
|
match self.op.key {
|
|
Key::Map(idx) => convert::Key::Prop(Cow::Owned(self.props.get(idx).into())),
|
|
Key::Seq(e) if e.is_head() => convert::Key::Elem(convert::ElemId::Head),
|
|
Key::Seq(ElemId(o)) => {
|
|
convert::Key::Elem(convert::ElemId::Op(translate(self.actor_lookup, &o)))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn val(&self) -> Cow<'a, crate::ScalarValue> {
|
|
match &self.op.action {
|
|
OpType::Put(v) => Cow::Borrowed(v),
|
|
OpType::Increment(i) => Cow::Owned(ScalarValue::Int(*i)),
|
|
_ => Cow::Owned(ScalarValue::Null),
|
|
}
|
|
}
|
|
|
|
fn succ(&self) -> Self::SuccIter {
|
|
OpAsDocOpSuccIter {
|
|
op: self.op,
|
|
offset: 0,
|
|
actor_index: self.actor_lookup,
|
|
}
|
|
}
|
|
|
|
fn insert(&self) -> bool {
|
|
self.op.insert
|
|
}
|
|
|
|
fn action(&self) -> u64 {
|
|
self.op.action.action_index()
|
|
}
|
|
}
|
|
|
|
pub(crate) struct OpAsDocOpSuccIter<'a> {
|
|
op: &'a Op,
|
|
offset: usize,
|
|
actor_index: &'a [usize],
|
|
}
|
|
|
|
impl<'a> Iterator for OpAsDocOpSuccIter<'a> {
|
|
type Item = DocOpId;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if let Some(s) = self.op.succ.get(self.offset) {
|
|
self.offset += 1;
|
|
Some(translate(self.actor_index, s))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> ExactSizeIterator for OpAsDocOpSuccIter<'a> {
|
|
fn len(&self) -> usize {
|
|
self.op.succ.len()
|
|
}
|
|
}
|
|
|
|
fn translate<'a>(actor_lookup: &'a [usize], op: &'a OpId) -> DocOpId {
|
|
let index = actor_lookup[op.actor()];
|
|
DocOpId {
|
|
actor: index,
|
|
counter: op.counter(),
|
|
}
|
|
}
|