use super::metadata::{Address, Entries, Entry, Index, Indices, Owner, Perm};
use crate::{Error, PublicKey, Result};
use crdts::{lseq::LSeq, CmRDT};
pub use crdts::{lseq::Op, Actor};
use serde::{Deserialize, Serialize};
use std::{
fmt::{self, Display},
hash::Hash,
};
const LSEQ_BOUNDARY: u64 = 1;
const LSEQ_TREE_BASE: u8 = 10;
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd)]
pub struct SequenceCrdt<A, P>
where
A: Actor,
P: Perm + Hash + Clone,
{
address: Address,
data: LSeq<Entry, A>,
permissions: LSeq<P, A>,
owners: LSeq<Owner, A>,
}
impl<A, P> Display for SequenceCrdt<A, P>
where
A: Actor,
P: Perm + Hash + Clone,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[")?;
for (i, entry) in self.data.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "<{}>", String::from_utf8_lossy(&entry),)?;
}
write!(f, "]")
}
}
impl<A, P> SequenceCrdt<A, P>
where
A: Actor,
P: Perm + Hash + Clone,
{
pub fn new(actor: A, address: Address) -> Self {
Self {
address,
data: LSeq::new_with_args(actor.clone(), LSEQ_TREE_BASE, LSEQ_BOUNDARY),
permissions: LSeq::new_with_args(actor.clone(), LSEQ_TREE_BASE, LSEQ_BOUNDARY),
owners: LSeq::new_with_args(actor, LSEQ_TREE_BASE, LSEQ_BOUNDARY),
}
}
pub fn address(&self) -> &Address {
&self.address
}
pub fn entries_index(&self) -> u64 {
self.data.len() as u64
}
pub fn owners_index(&self) -> u64 {
self.owners.len() as u64
}
pub fn permissions_index(&self) -> u64 {
self.permissions.len() as u64
}
pub fn append(&mut self, entry: Entry) -> Op<Entry, A> {
self.data.append(entry)
}
pub fn apply_crdt_op(&mut self, op: Op<Entry, A>) {
self.data.apply(op)
}
pub fn get(&self, index: Index) -> Option<&Entry> {
let i = to_absolute_index(index, self.entries_index() as usize)?;
self.data.get(i)
}
pub fn last_entry(&self) -> Option<&Entry> {
self.data.last()
}
pub fn permissions(&self, index: impl Into<Index>) -> Option<&P> {
let index = to_absolute_index(index.into(), self.permissions.len())?;
self.permissions.get(index)
}
pub fn owner(&self, owners_index: impl Into<Index>) -> Option<&Owner> {
let index = to_absolute_index(owners_index.into(), self.owners.len())?;
self.owners.get(index)
}
pub fn in_range(&self, start: Index, end: Index) -> Option<Entries> {
let start_index = to_absolute_index(start, self.entries_index() as usize)?;
let end_index = to_absolute_index(end, self.entries_index() as usize)?;
let range = self
.data
.iter()
.enumerate()
.filter_map(|(i, entry)| {
if i >= start_index && i < end_index {
Some(entry.clone())
} else {
None
}
})
.collect::<Entries>();
if range.is_empty() {
None
} else {
Some(range)
}
}
pub fn indices(&self) -> Result<Indices> {
Ok(Indices::new(
self.entries_index(),
self.owners_index(),
self.permissions_index(),
))
}
pub fn append_permissions(&mut self, permissions: P) -> Op<P, A> {
self.permissions.append(permissions)
}
pub fn apply_crdt_perms_op(&mut self, op: Op<P, A>) {
self.permissions.apply(op)
}
pub fn append_owner(&mut self, public_key: PublicKey) -> Op<Owner, A> {
self.owners.append(Owner {
entries_index: self.entries_index(),
permissions_index: self.permissions_index(),
public_key,
})
}
pub fn apply_crdt_owner_op(&mut self, op: Op<Owner, A>) {
self.owners.apply(op)
}
pub fn check_is_last_owner(&self, requester: PublicKey) -> Result<()> {
if self
.owner(Index::FromEnd(1))
.ok_or_else(|| Error::InvalidOwners)?
.public_key
== requester
{
Ok(())
} else {
Err(Error::AccessDenied)
}
}
}
fn to_absolute_index(index: Index, count: usize) -> Option<usize> {
match index {
Index::FromStart(index) if index as usize <= count => Some(index as usize),
Index::FromStart(_) => None,
Index::FromEnd(index) => count.checked_sub(index as usize),
}
}