cob: Simplify `Entry` and `Change` types
Instead of having two types, we simply have one, called `Entry`, which we rename from `Change`. There was no real benefit to having two almost identical types, but lots of added complexity. This patch simplifies the crate by removing one of the types.
This commit is contained in:
parent
f6bd7a3dc6
commit
205c15caa6
|
|
@ -36,7 +36,7 @@ pub fn run(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let root = patch.id.into();
|
||||
let root = *patch.id;
|
||||
let target = patch.target();
|
||||
|
||||
patch.transaction("Edit", &signer, |tx| {
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ pub fn timeline(
|
|||
|
||||
for (revision_id, revision) in patch.revisions() {
|
||||
// Don't show an "update" line for the first revision.
|
||||
if **revision_id != **patch_id {
|
||||
if *revision_id != **patch_id {
|
||||
timeline.push((
|
||||
revision.timestamp(),
|
||||
term::Line::spaced(
|
||||
|
|
|
|||
|
|
@ -12,11 +12,10 @@ use once_cell::sync::Lazy;
|
|||
use radicle_git_ext::commit::trailers::OwnedTrailer;
|
||||
|
||||
use crate::change::store::Version;
|
||||
use crate::history::entry::Timestamp;
|
||||
use crate::signatures;
|
||||
use crate::{
|
||||
change::{self, store, Change},
|
||||
history::entry,
|
||||
change,
|
||||
change::{store, Contents, Entry, Timestamp},
|
||||
signatures::{ExtendedSignature, Signatures},
|
||||
trailers, Embed,
|
||||
};
|
||||
|
|
@ -99,7 +98,7 @@ impl change::Storage for git2::Repository {
|
|||
parents: Vec<Self::Parent>,
|
||||
signer: &Signer,
|
||||
spec: store::Template<Self::ObjectId>,
|
||||
) -> Result<Change, Self::StoreError>
|
||||
) -> Result<Entry, Self::StoreError>
|
||||
where
|
||||
Signer: crypto::Signer,
|
||||
{
|
||||
|
|
@ -129,7 +128,7 @@ impl change::Storage for git2::Repository {
|
|||
tree,
|
||||
)?;
|
||||
|
||||
Ok(Change {
|
||||
Ok(Entry {
|
||||
id,
|
||||
revision: revision.into(),
|
||||
signature,
|
||||
|
|
@ -149,7 +148,7 @@ impl change::Storage for git2::Repository {
|
|||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> {
|
||||
fn load(&self, id: Self::ObjectId) -> Result<Entry, Self::LoadError> {
|
||||
let commit = Commit::read(self, id.into())?;
|
||||
let timestamp = git2::Time::from(commit.committer().time).seconds() as u64;
|
||||
let resource = parse_resource_trailer(commit.trailers())?;
|
||||
|
|
@ -172,7 +171,7 @@ impl change::Storage for git2::Repository {
|
|||
let manifest = load_manifest(self, &tree)?;
|
||||
let contents = load_contents(self, &tree)?;
|
||||
|
||||
Ok(Change {
|
||||
Ok(Entry {
|
||||
id,
|
||||
revision: tree.id().into(),
|
||||
signature: ExtendedSignature::new(key, sig),
|
||||
|
|
@ -220,10 +219,7 @@ fn load_manifest(
|
|||
})
|
||||
}
|
||||
|
||||
fn load_contents(
|
||||
repo: &git2::Repository,
|
||||
tree: &git2::Tree,
|
||||
) -> Result<entry::Contents, error::Load> {
|
||||
fn load_contents(repo: &git2::Repository, tree: &git2::Tree) -> Result<Contents, error::Load> {
|
||||
let ops = tree
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
use git_ext::Oid;
|
||||
|
||||
pub mod store;
|
||||
pub use store::{Storage, Template};
|
||||
pub use store::{Contents, EntryId, Storage, Template, Timestamp};
|
||||
|
||||
use crate::signatures::ExtendedSignature;
|
||||
|
||||
/// A single change in the change graph. The layout of changes in the repository
|
||||
/// is specified in the RFC (docs/rfc/0662-collaborative-objects.adoc)
|
||||
/// under "Change Commits".
|
||||
pub type Change = store::Change<Oid, Oid, ExtendedSignature>;
|
||||
pub type Entry = store::Entry<Oid, Oid, ExtendedSignature>;
|
||||
|
|
|
|||
|
|
@ -6,12 +6,9 @@ use nonempty::NonEmpty;
|
|||
use radicle_git_ext::Oid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
history::{Contents, Timestamp},
|
||||
signatures, TypeName,
|
||||
};
|
||||
use crate::{signatures, TypeName};
|
||||
|
||||
/// Change storage.
|
||||
/// Change entry storage.
|
||||
pub trait Storage {
|
||||
type StoreError: Error + Send + Sync + 'static;
|
||||
type LoadError: Error + Send + Sync + 'static;
|
||||
|
|
@ -20,7 +17,7 @@ pub trait Storage {
|
|||
type Parent;
|
||||
type Signatures;
|
||||
|
||||
/// Store a new change.
|
||||
/// Store a new change entry.
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn store<G>(
|
||||
&self,
|
||||
|
|
@ -28,16 +25,16 @@ pub trait Storage {
|
|||
parents: Vec<Self::Parent>,
|
||||
signer: &G,
|
||||
template: Template<Self::ObjectId>,
|
||||
) -> Result<Change<Self::Parent, Self::ObjectId, Self::Signatures>, Self::StoreError>
|
||||
) -> Result<Entry<Self::Parent, Self::ObjectId, Self::Signatures>, Self::StoreError>
|
||||
where
|
||||
G: crypto::Signer;
|
||||
|
||||
/// Load a change.
|
||||
/// Load a change entry.
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn load(
|
||||
&self,
|
||||
id: Self::ObjectId,
|
||||
) -> Result<Change<Self::Parent, Self::ObjectId, Self::Signatures>, Self::LoadError>;
|
||||
) -> Result<Entry<Self::Parent, Self::ObjectId, Self::Signatures>, Self::LoadError>;
|
||||
|
||||
/// Returns the parents of the object with the specified ID.
|
||||
fn parents_of(&self, id: &Oid) -> Result<Vec<Oid>, Self::LoadError>;
|
||||
|
|
@ -52,8 +49,18 @@ pub struct Template<Id> {
|
|||
pub contents: NonEmpty<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Entry contents.
|
||||
/// This is the change payload.
|
||||
pub type Contents = NonEmpty<Vec<u8>>;
|
||||
|
||||
/// Local time in seconds since epoch.
|
||||
pub type Timestamp = u64;
|
||||
|
||||
/// A unique identifier for a history entry.
|
||||
pub type EntryId = Oid;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Change<Resource, Id, Signature> {
|
||||
pub struct Entry<Resource, Id, Signature> {
|
||||
/// The content address of the `Change` itself.
|
||||
pub id: Id,
|
||||
/// The content address of the tree of the `Change`.
|
||||
|
|
@ -75,7 +82,7 @@ pub struct Change<Resource, Id, Signature> {
|
|||
pub timestamp: Timestamp,
|
||||
}
|
||||
|
||||
impl<Resource, Id, S> fmt::Display for Change<Resource, Id, S>
|
||||
impl<Resource, Id, S> fmt::Display for Entry<Resource, Id, S>
|
||||
where
|
||||
Id: fmt::Display,
|
||||
{
|
||||
|
|
@ -84,7 +91,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<Resource, Id, Signatures> Change<Resource, Id, Signatures> {
|
||||
impl<Resource, Id, Signatures> Entry<Resource, Id, Signatures> {
|
||||
pub fn id(&self) -> &Id {
|
||||
&self.id
|
||||
}
|
||||
|
|
@ -102,7 +109,7 @@ impl<Resource, Id, Signatures> Change<Resource, Id, Signatures> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R, Id> Change<R, Id, signatures::Signatures>
|
||||
impl<R, Id> Entry<R, Id, signatures::Signatures>
|
||||
where
|
||||
Id: AsRef<[u8]>,
|
||||
{
|
||||
|
|
@ -113,13 +120,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<R, Id> Change<R, Id, signatures::ExtendedSignature>
|
||||
impl<R, Id> Entry<R, Id, signatures::ExtendedSignature>
|
||||
where
|
||||
Id: AsRef<[u8]>,
|
||||
{
|
||||
pub fn valid_signatures(&self) -> bool {
|
||||
self.signature.verify(self.revision.as_ref())
|
||||
}
|
||||
|
||||
pub fn author(&self) -> &crypto::PublicKey {
|
||||
&self.signature.key
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ use git_ext::Oid;
|
|||
use radicle_dag::Dag;
|
||||
|
||||
use crate::{
|
||||
change, history::EntryId, object, signatures::ExtendedSignature, Change, CollaborativeObject,
|
||||
Entry, History, ObjectId, TypeName,
|
||||
change, history::EntryId, object, signatures::ExtendedSignature, CollaborativeObject, Entry,
|
||||
History, ObjectId, TypeName,
|
||||
};
|
||||
|
||||
/// The graph of changes for a particular collaborative object
|
||||
pub(super) struct ChangeGraph {
|
||||
object_id: ObjectId,
|
||||
graph: Dag<Oid, Change>,
|
||||
graph: Dag<Oid, Entry>,
|
||||
}
|
||||
|
||||
impl ChangeGraph {
|
||||
|
|
@ -96,20 +96,7 @@ impl ChangeGraph {
|
|||
if !change.valid_signatures() {
|
||||
return ControlFlow::Break(graph);
|
||||
}
|
||||
let entry = Entry::new(
|
||||
*change.id(),
|
||||
change.signature.key,
|
||||
change.resource,
|
||||
change.contents().clone(),
|
||||
change
|
||||
.parents
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|oid| oid.into())
|
||||
.collect(),
|
||||
change.timestamp,
|
||||
change.manifest.clone(),
|
||||
);
|
||||
let entry = change.value.clone();
|
||||
let id = *entry.id();
|
||||
|
||||
graph.node(id, entry);
|
||||
|
|
@ -141,7 +128,7 @@ impl ChangeGraph {
|
|||
}
|
||||
|
||||
struct GraphBuilder {
|
||||
graph: Dag<Oid, Change>,
|
||||
graph: Dag<Oid, Entry>,
|
||||
}
|
||||
|
||||
impl Default for GraphBuilder {
|
||||
|
|
@ -157,7 +144,7 @@ impl GraphBuilder {
|
|||
&mut self,
|
||||
storage: &S,
|
||||
commit_id: Oid,
|
||||
change: Change,
|
||||
change: Entry,
|
||||
) -> Result<Vec<(Oid, Oid)>, S::LoadError>
|
||||
where
|
||||
S: change::Storage<ObjectId = Oid, Parent = Oid, Signatures = ExtendedSignature>,
|
||||
|
|
|
|||
|
|
@ -3,13 +3,9 @@
|
|||
use std::{cmp::Ordering, collections::BTreeSet, ops::ControlFlow};
|
||||
|
||||
use git_ext::Oid;
|
||||
use radicle_crypto::PublicKey;
|
||||
use radicle_dag::Dag;
|
||||
|
||||
pub mod entry;
|
||||
pub use entry::{Contents, Entry, EntryId, Timestamp};
|
||||
|
||||
use crate::Manifest;
|
||||
pub use crate::change::{Contents, Entry, EntryId, Timestamp};
|
||||
|
||||
/// The DAG of changes making up the history of a collaborative object.
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -36,32 +32,11 @@ impl History {
|
|||
Self { root, graph }
|
||||
}
|
||||
|
||||
pub fn new_from_root<Id>(
|
||||
id: Id,
|
||||
actor: PublicKey,
|
||||
resource: Oid,
|
||||
contents: Contents,
|
||||
timestamp: Timestamp,
|
||||
manifest: Manifest,
|
||||
) -> Self
|
||||
where
|
||||
Id: Into<EntryId>,
|
||||
{
|
||||
let id = id.into();
|
||||
let root = Entry {
|
||||
id,
|
||||
actor,
|
||||
resource,
|
||||
contents,
|
||||
parents: vec![],
|
||||
timestamp,
|
||||
manifest,
|
||||
};
|
||||
/// Create a new history from a root entry.
|
||||
pub fn new_from_root(root: Entry) -> Self {
|
||||
let id = *root.id();
|
||||
|
||||
Self {
|
||||
root: id,
|
||||
graph: Dag::root(id, root),
|
||||
}
|
||||
Self::new(id, Dag::root(id, root))
|
||||
}
|
||||
|
||||
/// Get the current history timestamp.
|
||||
|
|
@ -69,17 +44,14 @@ impl History {
|
|||
pub fn timestamp(&self) -> Timestamp {
|
||||
self.graph
|
||||
.tips()
|
||||
.map(|(_, n)| n.timestamp())
|
||||
.map(|(_, n)| n.timestamp)
|
||||
.max()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Get all the tips of the graph.
|
||||
pub fn tips(&self) -> BTreeSet<Oid> {
|
||||
self.graph
|
||||
.tips()
|
||||
.map(|(_, entry)| (*entry.id()).into())
|
||||
.collect()
|
||||
self.graph.tips().map(|(_, entry)| *entry.id()).collect()
|
||||
}
|
||||
|
||||
/// A topological (parents before children) traversal of the dependency
|
||||
|
|
@ -109,34 +81,14 @@ impl History {
|
|||
}
|
||||
|
||||
/// Extend this history with a new entry.
|
||||
pub fn extend<Id>(
|
||||
&mut self,
|
||||
new_id: Id,
|
||||
new_actor: PublicKey,
|
||||
new_resource: Oid,
|
||||
new_contents: Contents,
|
||||
new_parents: Vec<EntryId>,
|
||||
new_timestamp: Timestamp,
|
||||
manifest: Manifest,
|
||||
) where
|
||||
Id: Into<EntryId>,
|
||||
{
|
||||
pub fn extend(&mut self, change: Entry) {
|
||||
let tips = self.tips();
|
||||
let new_id = new_id.into();
|
||||
let new_entry = Entry::new(
|
||||
new_id,
|
||||
new_actor,
|
||||
new_resource,
|
||||
new_contents,
|
||||
new_parents,
|
||||
new_timestamp,
|
||||
manifest,
|
||||
);
|
||||
let id = *change.id();
|
||||
|
||||
self.graph.node(new_id, new_entry);
|
||||
self.graph.node(id, change);
|
||||
|
||||
for tip in tips {
|
||||
self.graph.dependency(new_id, (*tip).into());
|
||||
self.graph.dependency(id, (*tip).into());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,162 +0,0 @@
|
|||
// Copyright © 2021 The Radicle Link Contributors
|
||||
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
|
||||
use git_ext::Oid;
|
||||
use nonempty::NonEmpty;
|
||||
use radicle_crypto::PublicKey;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{object, Manifest, ObjectId, Version};
|
||||
|
||||
/// Entry contents.
|
||||
/// This is the change payload.
|
||||
pub type Contents = NonEmpty<Vec<u8>>;
|
||||
|
||||
/// 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, Serialize, Deserialize)]
|
||||
pub struct EntryId(Oid);
|
||||
|
||||
impl fmt::Display for EntryId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for EntryId {
|
||||
type Err = git_ext::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let oid = git_ext::Oid::try_from(s)?;
|
||||
|
||||
Ok(Self(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
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&EntryId> for object::ObjectId {
|
||||
fn from(EntryId(id): &EntryId) -> Self {
|
||||
id.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectId> for EntryId {
|
||||
fn from(id: ObjectId) -> Self {
|
||||
Self(*id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for EntryId {
|
||||
type Target = Oid;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
/// Parent entries.
|
||||
pub(super) parents: Vec<EntryId>,
|
||||
/// The contents of this entry.
|
||||
pub(super) contents: Contents,
|
||||
/// The entry timestamp, as seconds since epoch.
|
||||
pub(super) timestamp: Timestamp,
|
||||
/// COB manifest.
|
||||
pub(super) manifest: Manifest,
|
||||
}
|
||||
|
||||
impl Entry {
|
||||
pub fn new<Id>(
|
||||
id: Id,
|
||||
actor: PublicKey,
|
||||
resource: Oid,
|
||||
contents: Contents,
|
||||
parents: Vec<EntryId>,
|
||||
timestamp: Timestamp,
|
||||
manifest: Manifest,
|
||||
) -> Self
|
||||
where
|
||||
Id: Into<EntryId>,
|
||||
{
|
||||
Self {
|
||||
id: id.into(),
|
||||
actor,
|
||||
resource,
|
||||
contents,
|
||||
timestamp,
|
||||
parents,
|
||||
manifest,
|
||||
}
|
||||
}
|
||||
|
||||
/// The current `Oid` of the resource this change lives under.
|
||||
pub fn resource(&self) -> Oid {
|
||||
self.resource
|
||||
}
|
||||
|
||||
/// Parent entries.
|
||||
pub fn parents(&self) -> &[EntryId] {
|
||||
&self.parents
|
||||
}
|
||||
|
||||
/// The public key of the actor.
|
||||
pub fn actor(&self) -> &PublicKey {
|
||||
&self.actor
|
||||
}
|
||||
|
||||
/// The COB version of this entry.
|
||||
pub fn version(&self) -> &Version {
|
||||
&self.manifest.version
|
||||
}
|
||||
|
||||
/// The COB manifest.
|
||||
pub fn manifest(&self) -> &Manifest {
|
||||
&self.manifest
|
||||
}
|
||||
|
||||
/// The entry timestamp.
|
||||
pub fn timestamp(&self) -> Timestamp {
|
||||
self.timestamp
|
||||
}
|
||||
|
||||
/// The contents of this change
|
||||
pub fn contents(&self) -> &Contents {
|
||||
&self.contents
|
||||
}
|
||||
|
||||
/// Entry ID.
|
||||
pub fn id(&self) -> &EntryId {
|
||||
&self.id
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,6 @@
|
|||
//! Collaborative objects are graphs of CRDTs. The current CRDTs that
|
||||
//! is intended to be used are specifically [automerge] CRDTs.
|
||||
//!
|
||||
//! The initial design is proposed at [RFC-0662], and this
|
||||
//! implementation keeps to most of its design principle.
|
||||
//!
|
||||
//! ## Basic Types
|
||||
//!
|
||||
//! The basic types that are found in `radicle-cob` are:
|
||||
|
|
@ -55,15 +52,14 @@
|
|||
//! automerge document and deserialize into an application defined
|
||||
//! object.
|
||||
//!
|
||||
//! This traversal is also the point at which the [`Entry::actor`]
|
||||
//! This traversal is also the point at which the [`Entry::author`]
|
||||
//! and [`Entry::resource`] can be retrieved to apply any kind of
|
||||
//! filtering logic. For example, a specific `actor`'s change may be
|
||||
//! filtering logic. For example, a specific `author`'s change may be
|
||||
//! egregious, spouting terrible libel about Radicle. It is at this
|
||||
//! point that the `actor`'s change can be filtered out from the
|
||||
//! final product of the traversal.
|
||||
//!
|
||||
//! [automerge]: https://automerge.org
|
||||
//! [RFC-0662]: https://github.com/radicle-dev/radicle-link/blob/master/docs/rfc/0662-collaborative-objects.adoc
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate qcheck;
|
||||
|
|
@ -81,11 +77,11 @@ mod change_graph;
|
|||
mod trailers;
|
||||
|
||||
pub mod change;
|
||||
pub use change::store::{Embed, Manifest, Version};
|
||||
pub use change::Change;
|
||||
pub use change::store::{Contents, Embed, Manifest, Version};
|
||||
pub use change::Entry;
|
||||
|
||||
pub mod history;
|
||||
pub use history::{Contents, Entry, History};
|
||||
pub use history::History;
|
||||
|
||||
pub mod signatures;
|
||||
use signatures::ExtendedSignature;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ impl Create {
|
|||
/// Create a new [`CollaborativeObject`].
|
||||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Change`]s at content-addressable locations. Please see
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
///
|
||||
/// The `signer` is expected to be a cryptographic signing key. This
|
||||
|
|
@ -74,14 +74,7 @@ where
|
|||
.update(identifier, &type_name, &object_id, &init_change)
|
||||
.map_err(|err| error::Create::Refs { err: Box::new(err) })?;
|
||||
|
||||
let history = History::new_from_root(
|
||||
*init_change.id(),
|
||||
init_change.signature.key,
|
||||
resource,
|
||||
init_change.contents,
|
||||
init_change.timestamp,
|
||||
init_change.manifest,
|
||||
);
|
||||
let history = History::new_from_root(init_change);
|
||||
|
||||
Ok(CollaborativeObject {
|
||||
manifest: Manifest::new(type_name, version),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use super::error;
|
|||
/// Get a [`CollaborativeObject`], if it exists.
|
||||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Change`]s at content-addressable locations. Please see
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
///
|
||||
/// The `typename` is the type of object to be found, while the
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ pub struct ChangeGraphInfo {
|
|||
/// is mostly useful for debugging and testing
|
||||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Change`]s at content-addressable locations. Please see
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
///
|
||||
/// The `typename` is the type of object to be found, while the `oid`
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use super::error;
|
|||
/// List a set of [`CollaborativeObject`].
|
||||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Change`]s at content-addressable locations. Please see
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
///
|
||||
/// The `typename` is the type of objects to be listed.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use super::error;
|
|||
/// Remove a [`crate::CollaborativeObject`].
|
||||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Change`]s at content-addressable locations. Please see
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
///
|
||||
/// The `typename` is the type of object to be found, while the
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ pub struct Update {
|
|||
/// Update an existing [`CollaborativeObject`].
|
||||
///
|
||||
/// The `storage` is the backing storage for storing
|
||||
/// [`crate::Change`]s at content-addressable locations. Please see
|
||||
/// [`crate::Entry`]s at content-addressable locations. Please see
|
||||
/// [`Store`] for further information.
|
||||
///
|
||||
/// The `signer` is expected to be a cryptographic signing key. This
|
||||
|
|
@ -99,21 +99,14 @@ where
|
|||
.update(identifier, typename, &object_id, &change)
|
||||
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
|
||||
|
||||
let parents: Vec<EntryId> = change.parents.into_iter().map(|oid| oid.into()).collect();
|
||||
let parents = change.parents.to_vec();
|
||||
let head = change.id;
|
||||
|
||||
object.history.extend(
|
||||
change.id,
|
||||
change.signature.key,
|
||||
change.resource,
|
||||
change.contents,
|
||||
parents.clone(),
|
||||
change.timestamp,
|
||||
change.manifest,
|
||||
);
|
||||
object.history.extend(change);
|
||||
|
||||
Ok(Updated {
|
||||
object,
|
||||
head: change.id,
|
||||
head,
|
||||
parents,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::{collections::BTreeMap, error::Error};
|
|||
use git_ext::ref_format::RefString;
|
||||
use git_ext::Oid;
|
||||
|
||||
use crate::change::Change;
|
||||
use crate::change::Entry;
|
||||
use crate::{ObjectId, TypeName};
|
||||
|
||||
/// The [`Reference`]s that refer to the commits that make up a
|
||||
|
|
@ -78,7 +78,7 @@ pub trait Storage {
|
|||
identifier: &Self::Identifier,
|
||||
typename: &TypeName,
|
||||
object_id: &ObjectId,
|
||||
change: &Change,
|
||||
change: &Entry,
|
||||
) -> Result<(), Self::UpdateError>;
|
||||
|
||||
/// Remove a ref to a particular collaborative object
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ impl change::Storage for Storage {
|
|||
signer: &Signer,
|
||||
spec: change::Template<Self::ObjectId>,
|
||||
) -> Result<
|
||||
change::store::Change<Self::Parent, Self::ObjectId, Self::Signatures>,
|
||||
change::store::Entry<Self::Parent, Self::ObjectId, Self::Signatures>,
|
||||
Self::StoreError,
|
||||
>
|
||||
where
|
||||
|
|
@ -85,10 +85,8 @@ impl change::Storage for Storage {
|
|||
fn load(
|
||||
&self,
|
||||
id: Self::ObjectId,
|
||||
) -> Result<
|
||||
change::store::Change<Self::Parent, Self::ObjectId, Self::Signatures>,
|
||||
Self::LoadError,
|
||||
> {
|
||||
) -> Result<change::store::Entry<Self::Parent, Self::ObjectId, Self::Signatures>, Self::LoadError>
|
||||
{
|
||||
self.as_raw().load(id)
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +154,7 @@ impl object::Storage for Storage {
|
|||
identifier: &Self::Identifier,
|
||||
typename: &crate::TypeName,
|
||||
object_id: &ObjectId,
|
||||
change: &change::Change,
|
||||
change: &change::Entry,
|
||||
) -> Result<(), Self::UpdateError> {
|
||||
let name = format!(
|
||||
"refs/rad/{}/cobs/{}/{}",
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ fn traverse_cobs() {
|
|||
|
||||
// traverse over the history and filter by changes that were only authorized by terry
|
||||
let contents = object.history().traverse(Vec::new(), |mut acc, _, entry| {
|
||||
if entry.actor() == terry_signer.public_key() {
|
||||
if entry.author() == terry_signer.public_key() {
|
||||
acc.push(entry.contents().head.clone());
|
||||
}
|
||||
ControlFlow::Continue(acc)
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ impl store::FromHistory for Issue {
|
|||
let root = history.root();
|
||||
|
||||
// Deprecated. Remove when we drop legacy support.
|
||||
if root.manifest().is_legacy() {
|
||||
if root.manifest.is_legacy() {
|
||||
let legacy = super::legacy::issue::Issue::from_history(history, repo)?;
|
||||
let issue = legacy.into();
|
||||
|
||||
|
|
@ -673,10 +673,10 @@ mod test {
|
|||
let mut issue_bob = bob_issues.get_mut(&id).unwrap();
|
||||
|
||||
issue_bob
|
||||
.comment("Bob's reply", id.into(), vec![], &t.bob.signer)
|
||||
.comment("Bob's reply", *id, vec![], &t.bob.signer)
|
||||
.unwrap();
|
||||
issue_alice
|
||||
.comment("Alice's reply", id.into(), vec![], &t.alice.signer)
|
||||
.comment("Alice's reply", *id, vec![], &t.alice.signer)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(issue_bob.comments().count(), 2);
|
||||
|
|
@ -704,7 +704,7 @@ mod test {
|
|||
t.eve.repo.fetch(&t.alice);
|
||||
|
||||
let eve_reply = issue_eve
|
||||
.comment("Eve's reply", id.into(), vec![], &t.eve.signer)
|
||||
.comment("Eve's reply", *id, vec![], &t.eve.signer)
|
||||
.unwrap();
|
||||
|
||||
t.bob.repo.fetch(&t.eve);
|
||||
|
|
@ -721,7 +721,7 @@ mod test {
|
|||
let (first, _) = issue_bob.comments().next().unwrap();
|
||||
let (last, _) = issue_bob.comments().last().unwrap();
|
||||
|
||||
assert_eq!(*first, issue_alice.id.into());
|
||||
assert_eq!(*first, *issue_alice.id);
|
||||
assert_eq!(*last, eve_reply);
|
||||
}
|
||||
|
||||
|
|
@ -1212,7 +1212,7 @@ mod test {
|
|||
issue
|
||||
.comment(
|
||||
"Here's a binary file",
|
||||
issue.id.into(),
|
||||
*issue.id,
|
||||
[embed3.clone()],
|
||||
&node.signer,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -84,11 +84,11 @@ impl From<Entry> for Op<Vec<u8>> {
|
|||
Self {
|
||||
id: *entry.id(),
|
||||
actions: entry.contents().clone(),
|
||||
author: *entry.actor(),
|
||||
parents: entry.parents().to_owned(),
|
||||
timestamp: Timestamp::from_secs(entry.timestamp()),
|
||||
identity: entry.resource(),
|
||||
manifest: entry.manifest().clone(),
|
||||
author: *entry.author(),
|
||||
parents: entry.parents.to_owned(),
|
||||
timestamp: Timestamp::from_secs(entry.timestamp),
|
||||
identity: entry.resource,
|
||||
manifest: entry.manifest.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -101,13 +101,13 @@ where
|
|||
|
||||
fn try_from(entry: &'a Entry) -> Result<Self, Self::Error> {
|
||||
let id = *entry.id();
|
||||
let identity = entry.resource();
|
||||
let identity = *entry.resource();
|
||||
let actions: Vec<_> = entry
|
||||
.contents()
|
||||
.iter()
|
||||
.map(|blob| serde_json::from_slice(blob.as_slice()))
|
||||
.collect::<Result<_, _>>()?;
|
||||
let manifest = entry.manifest().clone();
|
||||
let manifest = entry.manifest.clone();
|
||||
|
||||
// SAFETY: Entry is guaranteed to have at least one operation.
|
||||
#[allow(clippy::unwrap_used)]
|
||||
|
|
@ -115,9 +115,9 @@ where
|
|||
let op = Op {
|
||||
id,
|
||||
actions,
|
||||
author: *entry.actor(),
|
||||
timestamp: Timestamp::from_secs(entry.timestamp()),
|
||||
parents: entry.parents().to_owned(),
|
||||
author: *entry.author(),
|
||||
timestamp: Timestamp::from_secs(entry.timestamp),
|
||||
parents: entry.parents.to_owned(),
|
||||
identity,
|
||||
manifest,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -469,7 +469,7 @@ impl store::FromHistory for Patch {
|
|||
let root = history.root();
|
||||
|
||||
// Deprecated. Remove when we drop legacy support.
|
||||
if root.manifest().is_legacy() {
|
||||
if root.manifest.is_legacy() {
|
||||
let legacy = super::legacy::patch::Patch::from_history(history, repo)?;
|
||||
let patch = legacy.into();
|
||||
|
||||
|
|
@ -1379,7 +1379,7 @@ where
|
|||
revision: RevisionId,
|
||||
signer: &G,
|
||||
) -> Result<EntryId, Error> {
|
||||
if revision == RevisionId::from(self.id) {
|
||||
if revision == RevisionId::from(*self.id) {
|
||||
return Err(Error::RootRevision(revision));
|
||||
}
|
||||
self.transaction("Redact revision", signer, |tx| tx.redact(revision))
|
||||
|
|
@ -2223,7 +2223,7 @@ mod test {
|
|||
assert_eq!(patch.revisions().count(), 2);
|
||||
|
||||
patch.redact(revision_id, &alice.signer).unwrap();
|
||||
assert_eq!(patch.latest().0, &RevisionId::from(patch_id));
|
||||
assert_eq!(patch.latest().0, &RevisionId::from(*patch_id));
|
||||
assert_eq!(patch.revisions().count(), 1);
|
||||
|
||||
// The patch's root must always exist.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::marker::PhantomData;
|
|||
use std::ops::Deref;
|
||||
|
||||
use nonempty::NonEmpty;
|
||||
use radicle_crypto::ssh::ExtendedSignature;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::cob::op::Op;
|
||||
|
|
@ -58,18 +59,24 @@ where
|
|||
{
|
||||
pub fn new<G: Signer>(action: &T::Action, time: Timestamp, signer: &G) -> HistoryBuilder<T> {
|
||||
let resource = arbitrary::oid();
|
||||
let revision = arbitrary::oid();
|
||||
let (data, root) = encoded::<T, _>(action, time, [], signer);
|
||||
let manifest = Manifest::new(T::type_name().clone(), Version::default());
|
||||
let signature = signer.sign(data.as_slice());
|
||||
let signature = ExtendedSignature::new(*signer.public_key(), signature);
|
||||
let change = Entry {
|
||||
id: root,
|
||||
signature,
|
||||
resource,
|
||||
contents: NonEmpty::new(data),
|
||||
timestamp: time.as_secs(),
|
||||
revision,
|
||||
parents: vec![],
|
||||
manifest,
|
||||
};
|
||||
|
||||
Self {
|
||||
history: History::new_from_root(
|
||||
root,
|
||||
*signer.public_key(),
|
||||
resource,
|
||||
NonEmpty::new(data),
|
||||
time.as_secs(),
|
||||
manifest,
|
||||
),
|
||||
history: History::new_from_root(change),
|
||||
time,
|
||||
resource,
|
||||
witness: PhantomData,
|
||||
|
|
@ -87,18 +94,23 @@ where
|
|||
pub fn commit<G: Signer>(&mut self, action: &T::Action, signer: &G) -> git::ext::Oid {
|
||||
let timestamp = self.time;
|
||||
let tips = self.tips();
|
||||
let revision = arbitrary::oid();
|
||||
let (data, oid) = encoded::<T, _>(action, timestamp, tips, signer);
|
||||
let manifest = Manifest::new(T::type_name().clone(), Version::default());
|
||||
|
||||
self.history.extend(
|
||||
oid,
|
||||
*signer.public_key(),
|
||||
self.resource,
|
||||
NonEmpty::new(data),
|
||||
vec![],
|
||||
timestamp.as_secs(),
|
||||
let signature = signer.sign(data.as_slice());
|
||||
let signature = ExtendedSignature::new(*signer.public_key(), signature);
|
||||
let change = Entry {
|
||||
id: oid,
|
||||
signature,
|
||||
resource: self.resource,
|
||||
contents: NonEmpty::new(data),
|
||||
timestamp: timestamp.as_secs(),
|
||||
revision,
|
||||
parents: vec![],
|
||||
manifest,
|
||||
);
|
||||
};
|
||||
self.history.extend(change);
|
||||
|
||||
oid
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,14 +58,14 @@ impl change::Storage for Repository {
|
|||
parents: Vec<Self::Parent>,
|
||||
signer: &Signer,
|
||||
spec: change::Template<Self::ObjectId>,
|
||||
) -> Result<cob::Change, Self::StoreError>
|
||||
) -> Result<cob::Entry, Self::StoreError>
|
||||
where
|
||||
Signer: crypto::Signer,
|
||||
{
|
||||
self.backend.store(authority, parents, signer, spec)
|
||||
}
|
||||
|
||||
fn load(&self, id: Self::ObjectId) -> Result<cob::Change, Self::LoadError> {
|
||||
fn load(&self, id: Self::ObjectId) -> Result<cob::Entry, Self::LoadError> {
|
||||
self.backend.load(id)
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +139,7 @@ impl cob::object::Storage for Repository {
|
|||
identifier: &Self::Identifier,
|
||||
typename: &cob::TypeName,
|
||||
object_id: &cob::ObjectId,
|
||||
change: &cob::Change,
|
||||
change: &cob::Entry,
|
||||
) -> Result<(), Self::UpdateError> {
|
||||
self.backend.reference(
|
||||
git::refs::storage::cob(identifier, typename, object_id).as_str(),
|
||||
|
|
@ -202,14 +202,14 @@ impl<'a> change::Storage for DraftStore<'a> {
|
|||
parents: Vec<Self::Parent>,
|
||||
signer: &Signer,
|
||||
spec: change::Template<Self::ObjectId>,
|
||||
) -> Result<cob::Change, Self::StoreError>
|
||||
) -> Result<cob::Entry, Self::StoreError>
|
||||
where
|
||||
Signer: crypto::Signer,
|
||||
{
|
||||
self.repo.backend.store(authority, parents, signer, spec)
|
||||
}
|
||||
|
||||
fn load(&self, id: Self::ObjectId) -> Result<cob::Change, Self::LoadError> {
|
||||
fn load(&self, id: Self::ObjectId) -> Result<cob::Entry, Self::LoadError> {
|
||||
self.repo.backend.load(id)
|
||||
}
|
||||
|
||||
|
|
@ -387,7 +387,7 @@ impl<'a> cob::object::Storage for DraftStore<'a> {
|
|||
identifier: &Self::Identifier,
|
||||
typename: &cob::TypeName,
|
||||
object_id: &cob::ObjectId,
|
||||
change: &cob::Change,
|
||||
change: &cob::Entry,
|
||||
) -> Result<(), Self::UpdateError> {
|
||||
self.repo.backend.reference(
|
||||
git::refs::storage::draft::cob(identifier, typename, object_id).as_str(),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ pub fn oid() -> storage::Oid {
|
|||
}
|
||||
|
||||
pub fn entry_id() -> cob::EntryId {
|
||||
self::oid().into()
|
||||
self::oid()
|
||||
}
|
||||
|
||||
pub fn refstring(len: usize) -> git::RefString {
|
||||
|
|
|
|||
Loading…
Reference in New Issue