use crate::marks::{MarkSet, MarkStateMachine};
use crate::op_set::{Op, OpSetData};
use crate::op_tree::OpTreeNode;
use crate::query::{Index, ListState, MarkMap, QueryResult, TreeQuery};
use crate::types::Clock;
use crate::types::{ListEncoding, OpId};
use std::cmp::Ordering;
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct OpIdSearch<'a> {
list_state: ListState,
clock: Option<&'a Clock>,
target: SearchTarget<'a>,
marks: MarkMap<'a>,
}
#[derive(Debug, Clone, PartialEq)]
enum SearchTarget<'a> {
OpId(OpId, Option<Op<'a>>),
Op(Op<'a>),
Complete(usize),
}
impl<'a> OpIdSearch<'a> {
pub(crate) fn opid(target: OpId, encoding: ListEncoding, clock: Option<&'a Clock>) -> Self {
OpIdSearch {
list_state: ListState::new(encoding, usize::MAX),
clock,
target: SearchTarget::OpId(target, None),
marks: Default::default(),
}
}
pub(crate) fn op(op: Op<'a>, encoding: ListEncoding) -> Self {
let elemid = op
.key()
.elemid()
.expect("map op passed to query::OpIdSearch");
let target = match elemid.is_head() {
true => SearchTarget::Op(op),
false => SearchTarget::OpId(elemid.0, Some(op)),
};
OpIdSearch {
list_state: ListState::new(encoding, usize::MAX),
clock: None,
target,
marks: Default::default(),
}
}
pub(crate) fn found(&self) -> Option<usize> {
match &self.target {
SearchTarget::Complete(n) => Some(*n),
_ => None,
}
}
pub(crate) fn pos(&self) -> usize {
self.list_state.pos()
}
pub(crate) fn index(&self) -> usize {
self.list_state.index()
}
pub(crate) fn index_for(&self, op: Op<'a>) -> usize {
if self.list_state.was_last_seen(op.elemid_or_key()) {
self.list_state.last_index()
} else {
self.list_state.index()
}
}
pub(crate) fn marks(&self, osd: &OpSetData) -> Option<Arc<MarkSet>> {
let mut marks = MarkStateMachine::default();
for (id, mark_data) in self.marks.iter() {
marks.mark_begin(*id, mark_data, osd);
}
marks.current().cloned()
}
}
impl<'a> TreeQuery<'a> for OpIdSearch<'a> {
fn query_node(
&mut self,
child: &'a OpTreeNode,
index: &'a Index,
osd: &'a OpSetData,
) -> QueryResult {
self.list_state.check_if_node_is_clean(index);
if self.clock.is_some() {
QueryResult::Descend
} else {
match &self.target {
SearchTarget::OpId(id, _) if !index.ops.contains(id) => {
self.list_state
.process_node(child, index, osd, Some(&mut self.marks));
QueryResult::Next
}
_ => QueryResult::Descend,
}
}
}
fn query_element(&mut self, op: Op<'a>) -> QueryResult {
self.marks.process(op);
match self.target {
SearchTarget::OpId(target, None) => {
if op.id() == &target {
self.target = SearchTarget::Complete(self.list_state.pos());
return QueryResult::Finish;
}
}
SearchTarget::OpId(target, Some(op2)) => {
if op.id() == &target {
if op2.insert() {
self.target = SearchTarget::Op(op2);
} else {
self.target = SearchTarget::Complete(self.list_state.pos());
return QueryResult::Finish;
}
}
}
SearchTarget::Op(op2) => {
if op.insert() && op.lamport_cmp(*op2.id()) == Ordering::Less {
self.target = SearchTarget::Complete(self.list_state.pos());
return QueryResult::Finish;
}
}
SearchTarget::Complete(_) => return QueryResult::Finish, }
let elemid_or_key = op.elemid_or_key();
let visible_at = op.visible_at(self.clock);
self.list_state.process_op(op, elemid_or_key, visible_at);
QueryResult::Next
}
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct SimpleOpIdSearch<'a> {
target: OpId,
op: Op<'a>,
pub(crate) pos: usize,
found: bool,
}
impl<'a> SimpleOpIdSearch<'a> {
pub(crate) fn op(op: Op<'a>) -> Self {
let elemid = op
.key()
.elemid()
.expect("map op passed to query::OpIdSearch");
SimpleOpIdSearch {
target: elemid.0,
op,
pos: 0,
found: elemid.is_head(),
}
}
}
impl<'a> TreeQuery<'a> for SimpleOpIdSearch<'a> {
fn query_node(&mut self, child: &OpTreeNode, index: &Index, _osd: &OpSetData) -> QueryResult {
if self.found || index.ops.contains(&self.target) {
QueryResult::Descend
} else {
self.pos += child.len();
QueryResult::Next
}
}
fn query_element(&mut self, op: Op<'a>) -> QueryResult {
if !self.found {
if op.id() == &self.target {
self.found = true;
if !self.op.insert() {
return QueryResult::Finish;
}
}
} else if op.insert() && op.lamport_cmp(*self.op.id()) == Ordering::Less {
return QueryResult::Finish;
}
self.pos += 1;
QueryResult::Next
}
}