You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
5.3 KiB
Rust

use std::ops::{Bound, RangeBounds};
use crate::{Author, Change, Chronofold, FromLocalValue, LogIndex, Op, Timestamp};
/// An editing session tied to one author.
///
/// `Session` provides a lot of functions you might know from `Vec` or
/// `VecDeque`. Under the hood, `Session` will append changes to the
/// chronofolds log.
///
/// Note that `Session` has a mutable (exclusive) borrow of a chronofold. So
/// Rust's ownership rules enforce that there is always just one `Session` per
/// chronofold.
#[derive(Debug)]
pub struct Session<'a, A, T> {
chronofold: &'a mut Chronofold<A, T>,
author: A,
first_index: LogIndex,
}
impl<'a, A: Author, T> Session<'a, A, T> {
/// Creates an editing session for a single author.
pub fn new(author: A, chronofold: &'a mut Chronofold<A, T>) -> Self {
let first_index = chronofold.next_log_index();
Self {
chronofold,
author,
first_index,
}
}
/// Clears the chronofold, removing all elements.
pub fn clear(&mut self) {
let indices = self
.chronofold
.iter()
.map(|(_, idx)| idx)
.collect::<Vec<_>>();
for idx in indices {
self.remove(idx);
}
}
/// Appends an element to the back of the chronofold and returns the new
/// element's log index.
pub fn push_back(&mut self, value: T) -> LogIndex {
if let Some((_, last_index)) = self.chronofold.iter().last() {
self.insert_after(last_index, value)
} else {
// no non-deleted entries left
self.insert_after(self.as_ref().root, value)
}
}
/// Prepends an element to the chronofold and returns the new element's log
/// index.
pub fn push_front(&mut self, value: T) -> LogIndex {
self.insert_after(self.as_ref().root, value)
}
/// Inserts an element after the element with log index `index` and returns
/// the new element's log index.
///
/// If `index == None`, the element will be inserted at the beginning.
pub fn insert_after(&mut self, index: LogIndex, value: T) -> LogIndex {
self.apply_change(index, Change::Insert(value))
}
/// Removes the element with log index `index` from the chronofold.
///
/// Note that this just marks the element as deleted, not actually modify
/// the log apart from appending a `Change::Delete`.
pub fn remove(&mut self, index: LogIndex) {
self.apply_change(index, Change::Delete);
}
/// Extends the chronofold with the contents of `iter`, returns the log
/// index of the last inserted element, if any.
pub fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) -> Option<LogIndex> {
let oob = LogIndex(self.chronofold.log.len());
self.splice(oob..oob, iter)
}
#[allow(clippy::needless_collect)] // collect is needed due to borrowing
/// Replaces the specified range in the chronofold with the given
/// `replace_with` iterator and returns the log index of the last inserted
/// element, if any.
pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Option<LogIndex>
where
I: IntoIterator<Item = T>,
R: RangeBounds<LogIndex>,
{
let last_idx = match range.start_bound() {
Bound::Unbounded => None,
Bound::Included(idx) => self.chronofold.index_before(*idx),
Bound::Excluded(idx) => Some(*idx),
}
.unwrap_or(self.as_ref().root);
let to_remove: Vec<LogIndex> = self
.chronofold
.iter_range(range)
.map(|(_, idx)| idx)
.collect();
for idx in to_remove.into_iter() {
self.remove(idx);
}
self.apply_changes(last_idx, replace_with.into_iter().map(Change::Insert))
}
pub fn create_root(&mut self) -> LogIndex {
let new_index = LogIndex(self.chronofold.log.len());
self.chronofold
.apply_change(Timestamp(new_index, self.author), None, Change::Root)
}
pub fn insert(&mut self, value: T) -> LogIndex {
let new_index = LogIndex(self.chronofold.log.len());
self.chronofold.apply_change(
Timestamp(new_index, self.author),
None,
Change::Insert(value),
)
}
fn apply_change(&mut self, reference: LogIndex, change: Change<T>) -> LogIndex {
self.apply_changes(reference, Some(change)).unwrap()
}
fn apply_changes<I>(&mut self, reference: LogIndex, changes: I) -> Option<LogIndex>
where
I: IntoIterator<Item = Change<T>>,
{
self.chronofold
.apply_local_changes(self.author, reference, changes)
}
/// Returns an iterator over ops in log order, that where created in this
/// session.
pub fn iter_ops<V>(&'a self) -> impl Iterator<Item = Op<A, V>> + 'a
where
V: FromLocalValue<'a, A, T> + 'a,
{
self.chronofold
.iter_ops(self.first_index..)
.filter(move |op| op.id.1 == self.author)
}
}
impl<A: Author, T> AsRef<Chronofold<A, T>> for Session<'_, A, T> {
fn as_ref(&self) -> &Chronofold<A, T> {
self.chronofold
}
}
impl<A: Author, T> AsMut<Chronofold<A, T>> for Session<'_, A, T> {
fn as_mut(&mut self) -> &mut Chronofold<A, T> {
self.chronofold
}
}