mergable 0.43.0

A library for user-friendly and efficient CRDTs.
Documentation
pub struct Context<F: crate::SequenceFactory = crate::TimeSequenceFactory> {
	sequence_factory: F,
	sequence: F::Sequence,
	session: Session,
}

impl Default for Context<crate::TimeSequenceFactory> {
	fn default() -> Self {
		Self::new()
	}
}

impl<SequenceFactory: crate::SequenceFactory + Default> Context<SequenceFactory> {
	pub fn new() -> Self {
		Context {
			sequence_factory: Default::default(),
			sequence: SequenceFactory::base(),
			session: Session::new(),
		}
	}
}

impl<SequenceFactory: crate::SequenceFactory> Context<SequenceFactory> {
	pub fn next_sequence(&mut self) -> Sequence<SequenceFactory> {
		let next = self.sequence_factory.acquire(self.sequence.clone());
		assert!(next > self.sequence);

		self.sequence = next.clone();

		Sequence {
			value: next,
			session: self.session.clone(),
			undo: 0,
		}
	}

	/** Start a new session.
	 *
	 * This updates the session in this `Context`. This is similar to starting a new `Context` but it ensures that the sequences are still monotonically increasing across sessions which can be important for `Mergable` objects with last-write-wins semantics.
	 */
	pub fn new_session(&mut self) {
		self.session = Session::new();
	}

	pub fn session(&self) -> Session {
		self.session.clone()
	}
}

pub struct Sequence<SequenceFactory: crate::SequenceFactory> {
	value: SequenceFactory::Sequence,
	session: Session,
	undo: u8,
}

impl<SequenceFactory: crate::SequenceFactory> Sequence<SequenceFactory> {
	pub fn zero() -> Self {
		Sequence {
			value: SequenceFactory::base(),
			session: Session(u64::min_value()),
			undo: 0,
		}
	}

	pub fn undo(&self) -> Result<Self, crate::RevertError> {
		let mut r = self.clone();
		r.undo = self.undo.checked_add(1)
			.ok_or(crate::RevertError::TooManyRedos)?;
		Ok(r)
	}
}

impl<T: crate::SequenceFactory> std::fmt::Debug for Sequence<T>
	where T::Sequence: std::fmt::Debug
{
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		let Sequence{value, session, undo} = self;
		write!(f, "Sequence({:?}, {:?}, {:?})", value, session, undo)
	}
}

impl<SequenceFactory: crate::SequenceFactory> Clone for Sequence<SequenceFactory> {
	fn clone(&self) -> Self {
		Sequence {
			value: self.value.clone(),
			session: self.session.clone(),
			undo: self.undo,
		}
	}
}

impl<SequenceFactory: crate::SequenceFactory> std::cmp::PartialEq for Sequence<SequenceFactory> {
	fn eq(&self, that: &Self) -> bool {
		let Sequence{value, session, undo} = self;
		(value, session, undo) == (&that.value, &that.session, &that.undo)
	}
}

impl<SequenceFactory: crate::SequenceFactory> std::cmp::Eq for Sequence<SequenceFactory> {
}

impl<SequenceFactory: crate::SequenceFactory> std::cmp::PartialOrd for Sequence<SequenceFactory> {
	fn partial_cmp(&self, that: &Self) -> Option<std::cmp::Ordering> {
		let Sequence{value, session, undo} = self;
		std::cmp::PartialOrd::partial_cmp(
			&(value, session, undo),
			&(&that.value, &that.session, &that.undo))
	}
}

impl<SequenceFactory: crate::SequenceFactory> std::cmp::Ord for Sequence<SequenceFactory> {
	fn cmp(&self, that: &Self) -> std::cmp::Ordering {
		let Sequence{value, session, undo} = self;
		(value, session, undo).cmp(&(&that.value, &that.session, &that.undo))
	}
}

impl<SequenceFactory: crate::SequenceFactory> std::hash::Hash for Sequence<SequenceFactory> {
	fn hash<H: std::hash::Hasher>(&self, h: &mut H) {
		let Sequence{value, session, undo} = self;
		value.hash(h);
		session.hash(h);
		undo.hash(h);
	}
}

#[derive(Clone,Eq,PartialEq,Hash,Ord,PartialOrd)]
pub struct Session(u64);

impl Session {
	pub fn new() -> Self {
		Self::unsafe_from_u64(rand::Rng::gen(&mut rand::thread_rng()))
	}

	/// Create a session from the provided value.
	///
	/// WARNING: If the same session id is used concurrently it may cause unspecified behaviour including crashes.
	pub fn unsafe_from_u64(session: u64) -> Self {
		Session(session)
	}
}

impl std::fmt::Debug for Session {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(f, "Session({:x})", self.0)
	}
}