use nonempty::NonEmpty;
use radicle_cob::Manifest;
use thiserror::Error;
use radicle_cob::history::{Entry, EntryId};
use radicle_crypto::PublicKey;
use crate::cob::Timestamp;
use crate::identity::DocAt;
use crate::storage::ReadRepository;
use crate::{git, identity};
/// The author of an [`Op`].
pub type ActorId = PublicKey;
/// Error decoding an operation from an entry.
#[derive(Error, Debug)]
pub enum OpEncodingError {
#[error("encoding failed: {0}")]
Encoding(#[from] serde_json::Error),
#[error("git: {0}")]
Git(#[from] git2::Error),
}
/// The `Op` is the operation that is applied onto a state to form a CRDT.
///
/// Everything that can be done in the system is represented by an `Op`.
/// Operations are applied to an accumulator to yield a final state.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Op {
/// Id of the entry under which this operation lives.
pub id: EntryId,
/// The action carried out by this operation.
pub actions: NonEmpty,
/// The author of the operation.
pub author: ActorId,
/// Timestamp of this operation.
pub timestamp: Timestamp,
/// Parent operations.
pub parents: Vec,
/// Related objects.
pub related: Vec,
/// Head of identity document committed to by this operation.
pub identity: Option,
/// Object manifest.
pub manifest: Manifest,
}
impl PartialOrd for Op {
fn partial_cmp(&self, other: &Self) -> Option {
Some(self.cmp(other))
}
}
impl Ord for Op {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.id.cmp(&other.id)
}
}
impl Op {
pub fn new(
id: EntryId,
actions: impl Into>,
author: ActorId,
timestamp: impl Into,
identity: Option,
manifest: Manifest,
) -> Self {
Self {
id,
actions: actions.into(),
author,
timestamp: timestamp.into(),
parents: vec![],
related: vec![],
identity,
manifest,
}
}
pub fn id(&self) -> EntryId {
self.id
}
pub fn identity_doc(
&self,
repo: &R,
) -> Result