automerge/rust/automerge/src/storage/convert/op_as_docop.rs
Alex Good dd3c6d1303
Move rust workspace into ./rust
After some discussion with PVH I realise that the repo structure in the
last reorg was very rust-centric. In an attempt to put each language on
a level footing move the rust code and project files into ./rust
2022-10-16 19:55:51 +01:00

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(),
}
}