radicle-heartwood-lfs/radicle-cob/src/history/entry.rs

201 lines
4.6 KiB
Rust

// Copyright © 2021 The Radicle Link Contributors
//
// This file is part of radicle-link, distributed under the GPLv3 with Radicle
// Linking Exception. For full terms see the included LICENSE file.
use git_ext::Oid;
use nonempty::NonEmpty;
use radicle_crypto::PublicKey;
use crate::pruning_fold;
/// Blob under an entry.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct EntryBlob {
/// The OID of the blob.
pub oid: Oid,
/// The blob data.
pub data: Vec<u8>,
}
impl<'r> From<git2::Blob<'r>> for EntryBlob {
fn from(blob: git2::Blob) -> Self {
Self {
oid: blob.id().into(),
data: blob.content().to_vec(),
}
}
}
/// Entry contents.
/// This is the change payload.
pub type Contents = NonEmpty<EntryBlob>;
/// Logical clock used to track causality in change graph.
pub type Clock = u64;
/// Local time in seconds since epoch.
pub type Timestamp = u64;
/// A unique identifier for a history entry.
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct EntryId(Oid);
impl From<git2::Oid> for EntryId {
fn from(id: git2::Oid) -> Self {
Self(id.into())
}
}
impl From<Oid> for EntryId {
fn from(id: Oid) -> Self {
Self(id)
}
}
impl From<EntryId> for Oid {
fn from(EntryId(id): EntryId) -> Self {
id
}
}
/// One entry in the dependency graph for a change
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Entry {
/// The identifier for this entry
pub(super) id: EntryId,
/// The actor that authored this entry.
pub(super) actor: PublicKey,
/// The content-address for the resource this entry lives under.
/// If the resource was updated, this should point to its latest version.
pub(super) resource: Oid,
/// The child entries for this entry.
pub(super) children: Vec<EntryId>,
/// The contents of this entry.
pub(super) contents: Contents,
/// The entry timestamp, as seconds since epoch.
pub(super) timestamp: Timestamp,
}
impl Entry {
pub fn new<Id1, Id2, ChildIds>(
id: Id1,
actor: PublicKey,
resource: Oid,
children: ChildIds,
contents: Contents,
timestamp: Timestamp,
) -> Self
where
Id1: Into<EntryId>,
Id2: Into<EntryId>,
ChildIds: IntoIterator<Item = Id2>,
{
Self {
id: id.into(),
actor,
resource,
children: children.into_iter().map(|id| id.into()).collect(),
contents,
timestamp,
}
}
/// The ids of the changes this change depends on
pub fn children(&self) -> impl Iterator<Item = &EntryId> {
self.children.iter()
}
/// The current `Oid` of the resource this change lives under.
pub fn resource(&self) -> Oid {
self.resource
}
/// The public key of the actor.
pub fn actor(&self) -> &PublicKey {
&self.actor
}
/// The entry timestamp.
pub fn timestamp(&self) -> Timestamp {
self.timestamp
}
/// The contents of this change
pub fn contents(&self) -> &Contents {
&self.contents
}
pub fn id(&self) -> &EntryId {
&self.id
}
}
impl pruning_fold::GraphNode for Entry {
type Id = EntryId;
fn id(&self) -> &Self::Id {
&self.id
}
fn child_ids(&self) -> &[Self::Id] {
&self.children
}
}
/// Wraps an [`Entry`], adding a logical clock to it.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct EntryWithClock {
pub entry: Entry,
pub clock: Clock,
}
impl EntryWithClock {
pub fn root(entry: Entry) -> Self {
Self {
entry,
clock: 1 as Clock, // The root entry has a clock value of `1`.
}
}
}
impl EntryWithClock {
/// Get the clock value.
pub fn clock(&self) -> Clock {
self.clock
}
/// Get the clock range.
pub fn range(&self) -> std::ops::RangeInclusive<Clock> {
self.clock..=(self.clock + self.contents.tail.len() as Clock)
}
/// Iterator over the changes, including the clock.
pub fn changes(&self) -> impl Iterator<Item = (Clock, &EntryBlob)> {
self.contents
.iter()
.enumerate()
.map(|(ix, blob)| (self.clock + ix as u64, blob))
}
}
impl pruning_fold::GraphNode for EntryWithClock {
type Id = EntryId;
fn id(&self) -> &Self::Id {
&self.entry.id
}
fn child_ids(&self) -> &[Self::Id] {
&self.entry.children
}
}
impl std::ops::Deref for EntryWithClock {
type Target = Entry;
fn deref(&self) -> &Self::Target {
&self.entry
}
}