cob: Remove `author` from `Entry`
It was no longer used. Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
5a47023ac0
commit
dd7052926a
|
|
@ -28,7 +28,6 @@ pub mod error {
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::signatures::error::Signatures;
|
use crate::signatures::error::Signatures;
|
||||||
use crate::trailers;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Create {
|
pub enum Create {
|
||||||
|
|
@ -69,8 +68,6 @@ pub mod error {
|
||||||
#[error("the 'change' found at '{0}' has more than one signature")]
|
#[error("the 'change' found at '{0}' has more than one signature")]
|
||||||
TooManySignatures(Oid),
|
TooManySignatures(Oid),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
AuthorTrailer(#[from] trailers::error::InvalidAuthorTrailer),
|
|
||||||
#[error(transparent)]
|
|
||||||
ResourceTrailer(#[from] super::trailers::error::InvalidResourceTrailer),
|
ResourceTrailer(#[from] super::trailers::error::InvalidResourceTrailer),
|
||||||
#[error("non utf-8 characters in commit message")]
|
#[error("non utf-8 characters in commit message")]
|
||||||
Utf8(#[from] FromUtf8Error),
|
Utf8(#[from] FromUtf8Error),
|
||||||
|
|
@ -84,13 +81,11 @@ impl change::Storage for git2::Repository {
|
||||||
type LoadError = error::Load;
|
type LoadError = error::Load;
|
||||||
|
|
||||||
type ObjectId = Oid;
|
type ObjectId = Oid;
|
||||||
type Author = Oid;
|
|
||||||
type Resource = Oid;
|
type Resource = Oid;
|
||||||
type Signatures = Signature;
|
type Signatures = Signature;
|
||||||
|
|
||||||
fn create<Signer>(
|
fn create<Signer>(
|
||||||
&self,
|
&self,
|
||||||
author: Option<Self::Author>,
|
|
||||||
resource: Self::Resource,
|
resource: Self::Resource,
|
||||||
signer: &Signer,
|
signer: &Signer,
|
||||||
spec: store::Create<Self::ObjectId>,
|
spec: store::Create<Self::ObjectId>,
|
||||||
|
|
@ -119,20 +114,11 @@ impl change::Storage for git2::Repository {
|
||||||
Signature::from((*key, sig))
|
Signature::from((*key, sig))
|
||||||
};
|
};
|
||||||
|
|
||||||
let id = write_commit(
|
let id = write_commit(self, resource, tips, message, signature.clone(), tree)?;
|
||||||
self,
|
|
||||||
author,
|
|
||||||
resource,
|
|
||||||
tips,
|
|
||||||
message,
|
|
||||||
signature.clone(),
|
|
||||||
tree,
|
|
||||||
)?;
|
|
||||||
Ok(Change {
|
Ok(Change {
|
||||||
id,
|
id,
|
||||||
revision: revision.into(),
|
revision: revision.into(),
|
||||||
signature,
|
signature,
|
||||||
author,
|
|
||||||
resource,
|
resource,
|
||||||
manifest,
|
manifest,
|
||||||
contents,
|
contents,
|
||||||
|
|
@ -141,7 +127,7 @@ impl change::Storage for git2::Repository {
|
||||||
|
|
||||||
fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> {
|
fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> {
|
||||||
let commit = Commit::read(self, id.into())?;
|
let commit = Commit::read(self, id.into())?;
|
||||||
let (author, resource) = parse_trailers(commit.trailers())?;
|
let resource = parse_resource_trailer(commit.trailers())?;
|
||||||
let mut signatures = Signatures::try_from(&commit)?
|
let mut signatures = Signatures::try_from(&commit)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
@ -160,7 +146,6 @@ impl change::Storage for git2::Repository {
|
||||||
id,
|
id,
|
||||||
revision: tree.id().into(),
|
revision: tree.id().into(),
|
||||||
signature: signature.into(),
|
signature: signature.into(),
|
||||||
author,
|
|
||||||
resource,
|
resource,
|
||||||
manifest,
|
manifest,
|
||||||
contents,
|
contents,
|
||||||
|
|
@ -168,26 +153,21 @@ impl change::Storage for git2::Repository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_trailers<'a>(
|
fn parse_resource_trailer<'a>(
|
||||||
mut trailers: impl Iterator<Item = &'a OwnedTrailer>,
|
trailers: impl Iterator<Item = &'a OwnedTrailer>,
|
||||||
) -> Result<(Option<Oid>, Oid), error::Load> {
|
) -> Result<Oid, error::Load> {
|
||||||
let (author, resource) = trailers.try_fold((None, None), |(author, resource), trailer| {
|
for trailer in trailers {
|
||||||
match trailers::AuthorCommitTrailer::try_from(trailer) {
|
match trailers::ResourceCommitTrailer::try_from(trailer) {
|
||||||
Ok(trailer) => Ok((Some(trailer.oid().into()), resource)),
|
Err(trailers::error::InvalidResourceTrailer::WrongToken) => {
|
||||||
Err(err) => match err {
|
continue;
|
||||||
trailers::error::InvalidAuthorTrailer::NoTrailer
|
|
||||||
| trailers::error::InvalidAuthorTrailer::NoValue => Ok((author, resource)),
|
|
||||||
trailers::error::InvalidAuthorTrailer::WrongToken => {
|
|
||||||
let resource = trailers::ResourceCommitTrailer::try_from(trailer)?;
|
|
||||||
Ok((author, Some(resource.oid().into())))
|
|
||||||
}
|
}
|
||||||
err => Err(error::Load::from(err)),
|
Err(err) => return Err(err.into()),
|
||||||
},
|
Ok(resource) => return Ok(resource.oid().into()),
|
||||||
}
|
}
|
||||||
})?;
|
}
|
||||||
let resource = resource
|
Err(error::Load::from(
|
||||||
.ok_or_else(|| error::Load::from(trailers::error::InvalidResourceTrailer::NoTrailer))?;
|
trailers::error::InvalidResourceTrailer::NoTrailer,
|
||||||
Ok((author, resource))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_manifest(
|
fn load_manifest(
|
||||||
|
|
@ -223,7 +203,6 @@ fn load_contents(
|
||||||
|
|
||||||
fn write_commit<O>(
|
fn write_commit<O>(
|
||||||
repo: &git2::Repository,
|
repo: &git2::Repository,
|
||||||
author: Option<O>,
|
|
||||||
resource: O,
|
resource: O,
|
||||||
tips: Vec<O>,
|
tips: Vec<O>,
|
||||||
message: String,
|
message: String,
|
||||||
|
|
@ -233,16 +212,12 @@ fn write_commit<O>(
|
||||||
where
|
where
|
||||||
O: AsRef<git2::Oid>,
|
O: AsRef<git2::Oid>,
|
||||||
{
|
{
|
||||||
let author = author.map(|author| *author.as_ref());
|
|
||||||
let resource = *resource.as_ref();
|
let resource = *resource.as_ref();
|
||||||
|
|
||||||
let mut parents = tips.iter().map(|o| *o.as_ref()).collect::<Vec<_>>();
|
let mut parents = tips.iter().map(|o| *o.as_ref()).collect::<Vec<_>>();
|
||||||
parents.push(resource);
|
parents.push(resource);
|
||||||
parents.extend(author);
|
|
||||||
|
|
||||||
let mut trailers: Vec<OwnedTrailer> =
|
let trailers: Vec<OwnedTrailer> = vec![trailers::ResourceCommitTrailer::from(resource).into()];
|
||||||
vec![trailers::ResourceCommitTrailer::from(resource).into()];
|
|
||||||
trailers.extend(author.map(|author| trailers::AuthorCommitTrailer::from(author).into()));
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let author = repo.signature()?;
|
let author = repo.signature()?;
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,4 @@ use crate::signatures::Signature;
|
||||||
/// A single change in the change graph. The layout of changes in the repository
|
/// 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)
|
/// is specified in the RFC (docs/rfc/0662-collaborative-objects.adoc)
|
||||||
/// under "Change Commits".
|
/// under "Change Commits".
|
||||||
pub type Change = store::Change<Oid, Oid, Oid, Signature>;
|
pub type Change = store::Change<Oid, Oid, Signature>;
|
||||||
|
|
|
||||||
|
|
@ -14,21 +14,16 @@ pub trait Storage {
|
||||||
type LoadError: Error + Send + Sync + 'static;
|
type LoadError: Error + Send + Sync + 'static;
|
||||||
|
|
||||||
type ObjectId;
|
type ObjectId;
|
||||||
type Author;
|
|
||||||
type Resource;
|
type Resource;
|
||||||
type Signatures;
|
type Signatures;
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn create<Signer>(
|
fn create<Signer>(
|
||||||
&self,
|
&self,
|
||||||
author: Option<Self::Author>,
|
|
||||||
authority: Self::Resource,
|
authority: Self::Resource,
|
||||||
signer: &Signer,
|
signer: &Signer,
|
||||||
spec: Create<Self::ObjectId>,
|
spec: Create<Self::ObjectId>,
|
||||||
) -> Result<
|
) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::CreateError>
|
||||||
Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
|
|
||||||
Self::CreateError,
|
|
||||||
>
|
|
||||||
where
|
where
|
||||||
Signer: crypto::Signer;
|
Signer: crypto::Signer;
|
||||||
|
|
||||||
|
|
@ -36,10 +31,7 @@ pub trait Storage {
|
||||||
fn load(
|
fn load(
|
||||||
&self,
|
&self,
|
||||||
id: Self::ObjectId,
|
id: Self::ObjectId,
|
||||||
) -> Result<
|
) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::LoadError>;
|
||||||
Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
|
|
||||||
Self::LoadError,
|
|
||||||
>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Create<Id> {
|
pub struct Create<Id> {
|
||||||
|
|
@ -51,7 +43,7 @@ pub struct Create<Id> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Change<Author, Resource, Id, Signature> {
|
pub struct Change<Resource, Id, Signature> {
|
||||||
/// The content address of the `Change` itself.
|
/// The content address of the `Change` itself.
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
/// The content address of the tree of the `Change`.
|
/// The content address of the tree of the `Change`.
|
||||||
|
|
@ -59,9 +51,6 @@ pub struct Change<Author, Resource, Id, Signature> {
|
||||||
/// The cryptographic signature(s) and their public keys of the
|
/// The cryptographic signature(s) and their public keys of the
|
||||||
/// authors.
|
/// authors.
|
||||||
pub signature: Signature,
|
pub signature: Signature,
|
||||||
/// The author of this change. The `Author` is expected to be a
|
|
||||||
/// content address to look up the identity of the author.
|
|
||||||
pub author: Option<Author>,
|
|
||||||
/// The parent resource that this change lives under. For example,
|
/// The parent resource that this change lives under. For example,
|
||||||
/// this change could be for a patch of a project.
|
/// this change could be for a patch of a project.
|
||||||
pub resource: Resource,
|
pub resource: Resource,
|
||||||
|
|
@ -72,7 +61,7 @@ pub struct Change<Author, Resource, Id, Signature> {
|
||||||
pub contents: Contents,
|
pub contents: Contents,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Author, Resource, Id, S> fmt::Display for Change<Author, Resource, Id, S>
|
impl<Resource, Id, S> fmt::Display for Change<Resource, Id, S>
|
||||||
where
|
where
|
||||||
Id: fmt::Display,
|
Id: fmt::Display,
|
||||||
{
|
{
|
||||||
|
|
@ -81,15 +70,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Author, Resource, Id, Signatures> Change<Author, Resource, Id, Signatures> {
|
impl<Resource, Id, Signatures> Change<Resource, Id, Signatures> {
|
||||||
pub fn id(&self) -> &Id {
|
pub fn id(&self) -> &Id {
|
||||||
&self.id
|
&self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn author(&self) -> &Option<Author> {
|
|
||||||
&self.author
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn typename(&self) -> &TypeName {
|
pub fn typename(&self) -> &TypeName {
|
||||||
&self.manifest.typename
|
&self.manifest.typename
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +88,7 @@ impl<Author, Resource, Id, Signatures> Change<Author, Resource, Id, Signatures>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, R, Id> Change<A, R, Id, signatures::Signatures>
|
impl<R, Id> Change<R, Id, signatures::Signatures>
|
||||||
where
|
where
|
||||||
Id: AsRef<[u8]>,
|
Id: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
|
|
@ -114,7 +99,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, R, Id> Change<A, R, Id, signatures::Signature>
|
impl<R, Id> Change<R, Id, signatures::Signature>
|
||||||
where
|
where
|
||||||
Id: AsRef<[u8]>,
|
Id: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ impl ChangeGraph {
|
||||||
oid: &ObjectId,
|
oid: &ObjectId,
|
||||||
) -> Option<ChangeGraph>
|
) -> Option<ChangeGraph>
|
||||||
where
|
where
|
||||||
S: change::Storage<ObjectId = Oid, Author = Oid, Resource = Oid, Signatures = Signature>,
|
S: change::Storage<ObjectId = Oid, Resource = Oid, Signatures = Signature>,
|
||||||
{
|
{
|
||||||
log::info!("loading object '{}' '{}'", typename, oid);
|
log::info!("loading object '{}' '{}'", typename, oid);
|
||||||
let mut builder = GraphBuilder::default();
|
let mut builder = GraphBuilder::default();
|
||||||
|
|
@ -170,7 +170,6 @@ impl GraphBuilder {
|
||||||
commit: object::Commit,
|
commit: object::Commit,
|
||||||
change: Change,
|
change: Change,
|
||||||
) -> impl Iterator<Item = (object::Commit, Oid)> + '_ {
|
) -> impl Iterator<Item = (object::Commit, Oid)> + '_ {
|
||||||
let author_commit = *change.author();
|
|
||||||
let resource_commit = *change.resource();
|
let resource_commit = *change.resource();
|
||||||
let commit_id = commit.id;
|
let commit_id = commit.id;
|
||||||
if let Entry::Vacant(e) = self.node_indices.entry(commit_id) {
|
if let Entry::Vacant(e) = self.node_indices.entry(commit_id) {
|
||||||
|
|
@ -178,10 +177,7 @@ impl GraphBuilder {
|
||||||
e.insert(ix);
|
e.insert(ix);
|
||||||
}
|
}
|
||||||
commit.parents.into_iter().filter_map(move |parent| {
|
commit.parents.into_iter().filter_map(move |parent| {
|
||||||
if Some(parent.id) != author_commit
|
if parent.id != resource_commit && !self.has_edge(parent.id, commit_id) {
|
||||||
&& parent.id != resource_commit
|
|
||||||
&& !self.has_edge(parent.id, commit_id)
|
|
||||||
{
|
|
||||||
Some((parent, commit_id))
|
Some((parent, commit_id))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,6 @@ fn evaluate_change(
|
||||||
Ok(history::Entry::new(
|
Ok(history::Entry::new(
|
||||||
*change.id(),
|
*change.id(),
|
||||||
change.signature.key,
|
change.signature.key,
|
||||||
*change.author(),
|
|
||||||
change.resource,
|
change.resource,
|
||||||
child_commits.iter().cloned(),
|
child_commits.iter().cloned(),
|
||||||
change.contents().clone(),
|
change.contents().clone(),
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ impl History {
|
||||||
pub(crate) fn new_from_root<Id>(
|
pub(crate) fn new_from_root<Id>(
|
||||||
id: Id,
|
id: Id,
|
||||||
actor: PublicKey,
|
actor: PublicKey,
|
||||||
author: Option<Oid>,
|
|
||||||
resource: Oid,
|
resource: Oid,
|
||||||
contents: Contents,
|
contents: Contents,
|
||||||
) -> Self
|
) -> Self
|
||||||
|
|
@ -53,7 +52,6 @@ impl History {
|
||||||
let root_entry = Entry {
|
let root_entry = Entry {
|
||||||
id,
|
id,
|
||||||
actor,
|
actor,
|
||||||
author,
|
|
||||||
resource,
|
resource,
|
||||||
children: vec![],
|
children: vec![],
|
||||||
contents,
|
contents,
|
||||||
|
|
@ -120,7 +118,6 @@ impl History {
|
||||||
&mut self,
|
&mut self,
|
||||||
new_id: Id,
|
new_id: Id,
|
||||||
new_actor: PublicKey,
|
new_actor: PublicKey,
|
||||||
new_author: Option<Oid>,
|
|
||||||
new_resource: Oid,
|
new_resource: Oid,
|
||||||
new_contents: Contents,
|
new_contents: Contents,
|
||||||
) where
|
) where
|
||||||
|
|
@ -131,7 +128,6 @@ impl History {
|
||||||
let new_entry = Entry::new(
|
let new_entry = Entry::new(
|
||||||
new_id,
|
new_id,
|
||||||
new_actor,
|
new_actor,
|
||||||
new_author,
|
|
||||||
new_resource,
|
new_resource,
|
||||||
std::iter::empty::<git2::Oid>(),
|
std::iter::empty::<git2::Oid>(),
|
||||||
new_contents,
|
new_contents,
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,6 @@ pub struct Entry {
|
||||||
pub(super) id: EntryId,
|
pub(super) id: EntryId,
|
||||||
/// The actor that authored this entry.
|
/// The actor that authored this entry.
|
||||||
pub(super) actor: PublicKey,
|
pub(super) actor: PublicKey,
|
||||||
/// The content-address for this entry's author.
|
|
||||||
/// TODO: This shouldn't be here?
|
|
||||||
pub(super) author: Option<Oid>,
|
|
||||||
/// The content-address for the resource this entry lives under.
|
/// The content-address for the resource this entry lives under.
|
||||||
pub(super) resource: Oid,
|
pub(super) resource: Oid,
|
||||||
/// The child entries for this entry.
|
/// The child entries for this entry.
|
||||||
|
|
@ -60,7 +57,6 @@ impl Entry {
|
||||||
pub fn new<Id1, Id2, ChildIds>(
|
pub fn new<Id1, Id2, ChildIds>(
|
||||||
id: Id1,
|
id: Id1,
|
||||||
actor: PublicKey,
|
actor: PublicKey,
|
||||||
author: Option<Oid>,
|
|
||||||
resource: Oid,
|
resource: Oid,
|
||||||
children: ChildIds,
|
children: ChildIds,
|
||||||
contents: Contents,
|
contents: Contents,
|
||||||
|
|
@ -73,7 +69,6 @@ impl Entry {
|
||||||
Self {
|
Self {
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
actor,
|
actor,
|
||||||
author,
|
|
||||||
resource,
|
resource,
|
||||||
children: children.into_iter().map(|id| id.into()).collect(),
|
children: children.into_iter().map(|id| id.into()).collect(),
|
||||||
contents,
|
contents,
|
||||||
|
|
@ -95,11 +90,6 @@ impl Entry {
|
||||||
&self.actor
|
&self.actor
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `Oid` of the author that made this change.
|
|
||||||
pub fn author(&self) -> Option<&Oid> {
|
|
||||||
self.author.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The contents of this change
|
/// The contents of this change
|
||||||
pub fn contents(&self) -> &Contents {
|
pub fn contents(&self) -> &Contents {
|
||||||
&self.contents
|
&self.contents
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,10 @@ use git_ext::Oid;
|
||||||
/// An [`Identity`] represents a content addressed identity
|
/// An [`Identity`] represents a content addressed identity
|
||||||
/// (i.e. expected to be stored in a git backend).
|
/// (i.e. expected to be stored in a git backend).
|
||||||
///
|
///
|
||||||
/// It should have:
|
/// It should have a unique, stable, content addressable identifier.
|
||||||
/// * A delegate system
|
|
||||||
/// * A content addressable identifier
|
|
||||||
/// * A unique, stable identifier
|
|
||||||
pub trait Identity {
|
pub trait Identity {
|
||||||
type Identifier;
|
type Identifier;
|
||||||
|
|
||||||
/// Confirm that the given [`crypto::PublicKey`] is a delegate for
|
|
||||||
/// the identity.
|
|
||||||
fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool;
|
|
||||||
|
|
||||||
/// Provide the content address for the given identity. This is
|
/// Provide the content address for the given identity. This is
|
||||||
/// expected to be the latest address for the identity at the time
|
/// expected to be the latest address for the identity at the time
|
||||||
/// of use.
|
/// of use.
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,11 @@
|
||||||
//! automerge document and deserialize into an application defined
|
//! automerge document and deserialize into an application defined
|
||||||
//! object.
|
//! object.
|
||||||
//!
|
//!
|
||||||
//! This traversal is also the point at which the [`Entry::author`]
|
//! This traversal is also the point at which the [`Entry::actor`]
|
||||||
//! and [`Entry::resource`] can be retrieved to apply any kind of
|
//! and [`Entry::resource`] can be retrieved to apply any kind of
|
||||||
//! filtering logic. For example, a specific `author`'s change may be
|
//! filtering logic. For example, a specific `actor`'s change may be
|
||||||
//! egregious, spouting terrible libel about Radicle. It is at this
|
//! egregious, spouting terrible libel about Radicle. It is at this
|
||||||
//! point that the `author`'s change can be filtered out from the
|
//! point that the `actor`'s change can be filtered out from the
|
||||||
//! final product of the traversal.
|
//! final product of the traversal.
|
||||||
//!
|
//!
|
||||||
//! [automerge]: https://automerge.org
|
//! [automerge]: https://automerge.org
|
||||||
|
|
@ -132,7 +132,6 @@ where
|
||||||
CreateError = git::change::error::Create,
|
CreateError = git::change::error::Create,
|
||||||
LoadError = git::change::error::Load,
|
LoadError = git::change::error::Load,
|
||||||
ObjectId = git_ext::Oid,
|
ObjectId = git_ext::Oid,
|
||||||
Author = git_ext::Oid,
|
|
||||||
Resource = git_ext::Oid,
|
Resource = git_ext::Oid,
|
||||||
Signatures = Signature,
|
Signatures = Signature,
|
||||||
>,
|
>,
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,7 @@ use crate::Store;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// The metadata required for creating a new [`CollaborativeObject`].
|
/// The metadata required for creating a new [`CollaborativeObject`].
|
||||||
pub struct Create<Author> {
|
pub struct Create {
|
||||||
/// The identity of the author for this object's first change.
|
|
||||||
pub author: Option<Author>,
|
|
||||||
/// The type of history that will be used for this object.
|
/// The type of history that will be used for this object.
|
||||||
pub history_type: String,
|
pub history_type: String,
|
||||||
/// The CRDT history to initialize this object with.
|
/// The CRDT history to initialize this object with.
|
||||||
|
|
@ -21,7 +19,7 @@ pub struct Create<Author> {
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Author> Create<Author> {
|
impl Create {
|
||||||
fn create_spec(&self) -> change::Create<git_ext::Oid> {
|
fn create_spec(&self) -> change::Create<git_ext::Oid> {
|
||||||
change::Create {
|
change::Create {
|
||||||
typename: self.typename.clone(),
|
typename: self.typename.clone(),
|
||||||
|
|
@ -51,46 +49,31 @@ impl<Author> Create<Author> {
|
||||||
///
|
///
|
||||||
/// The `args` are the metadata for this [`CollaborativeObject`]. See
|
/// The `args` are the metadata for this [`CollaborativeObject`]. See
|
||||||
/// [`Create`] for further information.
|
/// [`Create`] for further information.
|
||||||
pub fn create<S, G, Author, Resource>(
|
pub fn create<S, G, Resource>(
|
||||||
storage: &S,
|
storage: &S,
|
||||||
signer: &G,
|
signer: &G,
|
||||||
resource: &Resource,
|
resource: &Resource,
|
||||||
identifier: &S::Identifier,
|
identifier: &S::Identifier,
|
||||||
args: Create<Author>,
|
args: Create,
|
||||||
) -> Result<CollaborativeObject, error::Create>
|
) -> Result<CollaborativeObject, error::Create>
|
||||||
where
|
where
|
||||||
S: Store,
|
S: Store,
|
||||||
G: crypto::Signer,
|
G: crypto::Signer,
|
||||||
Author: Identity,
|
|
||||||
Author::Identifier: Clone + PartialEq,
|
|
||||||
Resource: Identity,
|
Resource: Identity,
|
||||||
{
|
{
|
||||||
let Create {
|
let Create {
|
||||||
author,
|
|
||||||
ref contents,
|
ref contents,
|
||||||
ref typename,
|
ref typename,
|
||||||
..
|
..
|
||||||
} = &args;
|
} = &args;
|
||||||
|
|
||||||
let content = match author {
|
|
||||||
None => None,
|
|
||||||
Some(author) => {
|
|
||||||
if !author.is_delegate(signer.public_key()) {
|
|
||||||
return Err(error::Create::SignerIsNotAuthor);
|
|
||||||
} else {
|
|
||||||
Some(author.content_id())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let init_change = storage
|
let init_change = storage
|
||||||
.create(content, resource.content_id(), signer, args.create_spec())
|
.create(resource.content_id(), signer, args.create_spec())
|
||||||
.map_err(error::Create::from)?;
|
.map_err(error::Create::from)?;
|
||||||
|
|
||||||
let history = History::new_from_root(
|
let history = History::new_from_root(
|
||||||
*init_change.id(),
|
*init_change.id(),
|
||||||
init_change.signature.key,
|
init_change.signature.key,
|
||||||
content,
|
|
||||||
resource.content_id(),
|
resource.content_id(),
|
||||||
contents.clone(),
|
contents.clone(),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,7 @@ use crate::{
|
||||||
use super::error;
|
use super::error;
|
||||||
|
|
||||||
/// The data required to update an object
|
/// The data required to update an object
|
||||||
pub struct Update<Author> {
|
pub struct Update {
|
||||||
/// The identity of the author for the update of this object.
|
|
||||||
pub author: Option<Author>,
|
|
||||||
/// The type of history that will be used for this object.
|
/// The type of history that will be used for this object.
|
||||||
pub history_type: String,
|
pub history_type: String,
|
||||||
/// The CRDT changes to add to the object.
|
/// The CRDT changes to add to the object.
|
||||||
|
|
@ -44,22 +42,19 @@ pub struct Update<Author> {
|
||||||
///
|
///
|
||||||
/// The `args` are the metadata for this [`CollaborativeObject`]
|
/// The `args` are the metadata for this [`CollaborativeObject`]
|
||||||
/// udpate. See [`Update`] for further information.
|
/// udpate. See [`Update`] for further information.
|
||||||
pub fn update<S, G, Resource, Author>(
|
pub fn update<S, G, Resource>(
|
||||||
storage: &S,
|
storage: &S,
|
||||||
signer: &G,
|
signer: &G,
|
||||||
resource: &Resource,
|
resource: &Resource,
|
||||||
identifier: &S::Identifier,
|
identifier: &S::Identifier,
|
||||||
args: Update<Author>,
|
args: Update,
|
||||||
) -> Result<CollaborativeObject, error::Update>
|
) -> Result<CollaborativeObject, error::Update>
|
||||||
where
|
where
|
||||||
S: Store,
|
S: Store,
|
||||||
G: crypto::Signer,
|
G: crypto::Signer,
|
||||||
Author: Identity,
|
|
||||||
Author::Identifier: Clone + PartialEq,
|
|
||||||
Resource: Identity,
|
Resource: Identity,
|
||||||
{
|
{
|
||||||
let Update {
|
let Update {
|
||||||
author,
|
|
||||||
ref typename,
|
ref typename,
|
||||||
object_id,
|
object_id,
|
||||||
history_type,
|
history_type,
|
||||||
|
|
@ -67,18 +62,6 @@ where
|
||||||
message,
|
message,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
let author = match author {
|
|
||||||
None => None,
|
|
||||||
Some(author) => {
|
|
||||||
// TODO: Remove?
|
|
||||||
if !author.is_delegate(signer.public_key()) {
|
|
||||||
return Err(error::Update::SignerIsNotAuthor);
|
|
||||||
} else {
|
|
||||||
Some(author.content_id())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let existing_refs = storage
|
let existing_refs = storage
|
||||||
.objects(typename, &object_id)
|
.objects(typename, &object_id)
|
||||||
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
|
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
|
||||||
|
|
@ -88,7 +71,6 @@ where
|
||||||
.ok_or(error::Update::NoSuchObject)?;
|
.ok_or(error::Update::NoSuchObject)?;
|
||||||
|
|
||||||
let change = storage.create(
|
let change = storage.create(
|
||||||
author,
|
|
||||||
resource.content_id(),
|
resource.content_id(),
|
||||||
signer,
|
signer,
|
||||||
change::Create {
|
change::Create {
|
||||||
|
|
@ -99,13 +81,9 @@ where
|
||||||
message,
|
message,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
object.history.extend(
|
object
|
||||||
change.id,
|
.history
|
||||||
change.signature.key,
|
.extend(change.id, change.signature.key, change.resource, changes);
|
||||||
author,
|
|
||||||
change.resource,
|
|
||||||
changes,
|
|
||||||
);
|
|
||||||
storage
|
storage
|
||||||
.update(identifier, typename, &object_id, &change)
|
.update(identifier, typename, &object_id, &change)
|
||||||
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
|
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
|
||||||
|
|
|
||||||
|
|
@ -53,52 +53,14 @@ impl Person {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn key(&self) -> crypto::PublicKey {
|
|
||||||
self.payload.key
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> &Name {
|
pub fn name(&self) -> &Name {
|
||||||
&self.payload.name
|
&self.payload.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_oid(
|
|
||||||
repo: &git2::Repository,
|
|
||||||
id: Oid,
|
|
||||||
) -> Result<Option<Person>, storage::error::Identity> {
|
|
||||||
match repo.find_commit(id.into()) {
|
|
||||||
Ok(commit) => from_commit(repo, commit),
|
|
||||||
Err(err) if err.code() == git2::ErrorCode::NotFound => Ok(None),
|
|
||||||
Err(err) => Err(err.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_commit(
|
|
||||||
repo: &git2::Repository,
|
|
||||||
commit: git2::Commit,
|
|
||||||
) -> Result<Option<Person>, storage::error::Identity> {
|
|
||||||
let tree = commit.tree()?;
|
|
||||||
let entry = tree
|
|
||||||
.get_name("identity")
|
|
||||||
.ok_or_else(|| storage::error::Identity::NotFound(tree.id().into()))?;
|
|
||||||
let blob = match entry.to_object(repo)?.into_blob() {
|
|
||||||
Ok(blob) => blob,
|
|
||||||
Err(other) => return Err(storage::error::Identity::NotBlob(other.kind())),
|
|
||||||
};
|
|
||||||
let payload = serde_json::de::from_slice(blob.content())?;
|
|
||||||
Ok(Some(Person {
|
|
||||||
payload,
|
|
||||||
content_id: commit.id().into(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Identity for Person {
|
impl Identity for Person {
|
||||||
type Identifier = Urn;
|
type Identifier = Urn;
|
||||||
|
|
||||||
fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
|
|
||||||
self.key() == *delegation
|
|
||||||
}
|
|
||||||
|
|
||||||
fn content_id(&self) -> Oid {
|
fn content_id(&self) -> Oid {
|
||||||
self.content_id
|
self.content_id
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,56 +70,14 @@ impl Project {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delegates(&self) -> &BTreeSet<crypto::PublicKey> {
|
|
||||||
&self.payload.delegates
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delegate_check(&self, person: &test::Person) -> bool {
|
|
||||||
self.payload.delegates.contains(&person.key())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> &Name {
|
pub fn name(&self) -> &Name {
|
||||||
&self.payload.name
|
&self.payload.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_oid(
|
|
||||||
repo: &git2::Repository,
|
|
||||||
id: Oid,
|
|
||||||
) -> Result<Option<Self>, storage::error::Identity> {
|
|
||||||
match repo.find_commit(id.into()) {
|
|
||||||
Ok(commit) => from_commit(repo, commit),
|
|
||||||
Err(err) if err.code() == git2::ErrorCode::NotFound => Ok(None),
|
|
||||||
Err(err) => Err(err.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_commit(
|
|
||||||
repo: &git2::Repository,
|
|
||||||
commit: git2::Commit,
|
|
||||||
) -> Result<Option<Project>, storage::error::Identity> {
|
|
||||||
let tree = commit.tree()?;
|
|
||||||
let entry = tree
|
|
||||||
.get_name("identity")
|
|
||||||
.ok_or_else(|| storage::error::Identity::NotFound(tree.id().into()))?;
|
|
||||||
let blob = match entry.to_object(repo)?.into_blob() {
|
|
||||||
Ok(blob) => blob,
|
|
||||||
Err(other) => return Err(storage::error::Identity::NotBlob(other.kind())),
|
|
||||||
};
|
|
||||||
let payload = serde_json::de::from_slice(blob.content())?;
|
|
||||||
Ok(Some(Project {
|
|
||||||
payload,
|
|
||||||
content_id: commit.id().into(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Identity for RemoteProject {
|
impl Identity for RemoteProject {
|
||||||
type Identifier = Urn;
|
type Identifier = Urn;
|
||||||
|
|
||||||
fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
|
|
||||||
self.project.delegates().contains(delegation)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn content_id(&self) -> Oid {
|
fn content_id(&self) -> Oid {
|
||||||
self.project.content_id
|
self.project.content_id
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,6 @@ pub mod error {
|
||||||
Json(#[from] serde_json::Error),
|
Json(#[from] serde_json::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Git(#[from] git2::Error),
|
Git(#[from] git2::Error),
|
||||||
#[error("'identity' was not a blob, found '{0:?}'")]
|
|
||||||
NotBlob(Option<git2::ObjectType>),
|
|
||||||
#[error("could not find 'identity' in the tree '{0}'")]
|
|
||||||
NotFound(git_ext::Oid),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
@ -67,31 +63,29 @@ impl change::Storage for Storage {
|
||||||
type LoadError = <git2::Repository as change::Storage>::LoadError;
|
type LoadError = <git2::Repository as change::Storage>::LoadError;
|
||||||
|
|
||||||
type ObjectId = <git2::Repository as change::Storage>::ObjectId;
|
type ObjectId = <git2::Repository as change::Storage>::ObjectId;
|
||||||
type Author = <git2::Repository as change::Storage>::Author;
|
|
||||||
type Resource = <git2::Repository as change::Storage>::Resource;
|
type Resource = <git2::Repository as change::Storage>::Resource;
|
||||||
type Signatures = <git2::Repository as change::Storage>::Signatures;
|
type Signatures = <git2::Repository as change::Storage>::Signatures;
|
||||||
|
|
||||||
fn create<Signer>(
|
fn create<Signer>(
|
||||||
&self,
|
&self,
|
||||||
author: Option<Self::Author>,
|
|
||||||
authority: Self::Resource,
|
authority: Self::Resource,
|
||||||
signer: &Signer,
|
signer: &Signer,
|
||||||
spec: change::Create<Self::ObjectId>,
|
spec: change::Create<Self::ObjectId>,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
change::store::Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
|
change::store::Change<Self::Resource, Self::ObjectId, Self::Signatures>,
|
||||||
Self::CreateError,
|
Self::CreateError,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
Signer: crypto::Signer,
|
Signer: crypto::Signer,
|
||||||
{
|
{
|
||||||
self.as_raw().create(author, authority, signer, spec)
|
self.as_raw().create(authority, signer, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(
|
fn load(
|
||||||
&self,
|
&self,
|
||||||
id: Self::ObjectId,
|
id: Self::ObjectId,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
change::store::Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
|
change::store::Change<Self::Resource, Self::ObjectId, Self::Signatures>,
|
||||||
Self::LoadError,
|
Self::LoadError,
|
||||||
> {
|
> {
|
||||||
self.as_raw().load(id)
|
self.as_raw().load(id)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ fn roundtrip() {
|
||||||
let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
|
let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
|
||||||
let proj = test::RemoteProject {
|
let proj = test::RemoteProject {
|
||||||
project: proj,
|
project: proj,
|
||||||
person: terry.clone(),
|
person: terry,
|
||||||
};
|
};
|
||||||
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
||||||
let cob = create(
|
let cob = create(
|
||||||
|
|
@ -28,7 +28,6 @@ fn roundtrip() {
|
||||||
&proj,
|
&proj,
|
||||||
&proj.identifier(),
|
&proj.identifier(),
|
||||||
Create {
|
Create {
|
||||||
author: Some(terry),
|
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
contents: Vec::new(),
|
contents: Vec::new(),
|
||||||
typename: typename.clone(),
|
typename: typename.clone(),
|
||||||
|
|
@ -52,7 +51,7 @@ fn list_cobs() {
|
||||||
let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
|
let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
|
||||||
let proj = test::RemoteProject {
|
let proj = test::RemoteProject {
|
||||||
project: proj,
|
project: proj,
|
||||||
person: terry.clone(),
|
person: terry,
|
||||||
};
|
};
|
||||||
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
||||||
let issue_1 = create(
|
let issue_1 = create(
|
||||||
|
|
@ -61,7 +60,6 @@ fn list_cobs() {
|
||||||
&proj,
|
&proj,
|
||||||
&proj.identifier(),
|
&proj.identifier(),
|
||||||
Create {
|
Create {
|
||||||
author: Some(terry.clone()),
|
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
contents: b"issue 1".to_vec(),
|
contents: b"issue 1".to_vec(),
|
||||||
typename: typename.clone(),
|
typename: typename.clone(),
|
||||||
|
|
@ -76,7 +74,6 @@ fn list_cobs() {
|
||||||
&proj,
|
&proj,
|
||||||
&proj.identifier(),
|
&proj.identifier(),
|
||||||
Create {
|
Create {
|
||||||
author: Some(terry),
|
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
contents: b"issue 2".to_vec(),
|
contents: b"issue 2".to_vec(),
|
||||||
typename: typename.clone(),
|
typename: typename.clone(),
|
||||||
|
|
@ -102,7 +99,7 @@ fn update_cob() {
|
||||||
let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
|
let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
|
||||||
let proj = test::RemoteProject {
|
let proj = test::RemoteProject {
|
||||||
project: proj,
|
project: proj,
|
||||||
person: terry.clone(),
|
person: terry,
|
||||||
};
|
};
|
||||||
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
||||||
let cob = create(
|
let cob = create(
|
||||||
|
|
@ -111,7 +108,6 @@ fn update_cob() {
|
||||||
&proj,
|
&proj,
|
||||||
&proj.identifier(),
|
&proj.identifier(),
|
||||||
Create {
|
Create {
|
||||||
author: Some(terry.clone()),
|
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
contents: Vec::new(),
|
contents: Vec::new(),
|
||||||
typename: typename.clone(),
|
typename: typename.clone(),
|
||||||
|
|
@ -130,7 +126,6 @@ fn update_cob() {
|
||||||
&proj,
|
&proj,
|
||||||
&proj.identifier(),
|
&proj.identifier(),
|
||||||
Update {
|
Update {
|
||||||
author: Some(terry),
|
|
||||||
changes: b"issue 1".to_vec(),
|
changes: b"issue 1".to_vec(),
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
object_id: *cob.id(),
|
object_id: *cob.id(),
|
||||||
|
|
@ -158,11 +153,11 @@ fn traverse_cobs() {
|
||||||
let proj = test::Project::new(&storage, "discworld", *terry_signer.public_key()).unwrap();
|
let proj = test::Project::new(&storage, "discworld", *terry_signer.public_key()).unwrap();
|
||||||
let terry_proj = test::RemoteProject {
|
let terry_proj = test::RemoteProject {
|
||||||
project: proj.clone(),
|
project: proj.clone(),
|
||||||
person: terry.clone(),
|
person: terry,
|
||||||
};
|
};
|
||||||
let neil_proj = test::RemoteProject {
|
let neil_proj = test::RemoteProject {
|
||||||
project: proj,
|
project: proj,
|
||||||
person: neil.clone(),
|
person: neil,
|
||||||
};
|
};
|
||||||
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
||||||
let cob = create(
|
let cob = create(
|
||||||
|
|
@ -171,7 +166,6 @@ fn traverse_cobs() {
|
||||||
&terry_proj,
|
&terry_proj,
|
||||||
&terry_proj.identifier(),
|
&terry_proj.identifier(),
|
||||||
Create {
|
Create {
|
||||||
author: Some(terry),
|
|
||||||
contents: b"issue 1".to_vec(),
|
contents: b"issue 1".to_vec(),
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
typename: typename.clone(),
|
typename: typename.clone(),
|
||||||
|
|
@ -194,7 +188,6 @@ fn traverse_cobs() {
|
||||||
&neil_proj,
|
&neil_proj,
|
||||||
&neil_proj.identifier(),
|
&neil_proj.identifier(),
|
||||||
Update {
|
Update {
|
||||||
author: Some(neil),
|
|
||||||
changes: b"issue 2".to_vec(),
|
changes: b"issue 2".to_vec(),
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
object_id: *cob.id(),
|
object_id: *cob.id(),
|
||||||
|
|
@ -206,16 +199,8 @@ fn traverse_cobs() {
|
||||||
|
|
||||||
// traverse over the history and filter by changes that were only authorized by terry
|
// traverse over the history and filter by changes that were only authorized by terry
|
||||||
let contents = updated.history().traverse(Vec::new(), |mut acc, entry| {
|
let contents = updated.history().traverse(Vec::new(), |mut acc, entry| {
|
||||||
let author = match entry.author() {
|
if entry.actor() == terry_signer.public_key() {
|
||||||
Some(author) => test::Person::find_by_oid(storage.as_raw(), *author).unwrap(),
|
acc.push(entry.contents().to_vec());
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
let project = test::Project::find_by_oid(storage.as_raw(), entry.resource()).unwrap();
|
|
||||||
|
|
||||||
if let (Some(author), Some(project)) = (author, project) {
|
|
||||||
if project.delegate_check(&author) {
|
|
||||||
acc.push(entry.contents().to_vec())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ControlFlow::Continue(acc)
|
ControlFlow::Continue(acc)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,14 @@
|
||||||
// This file is part of radicle-link, distributed under the GPLv3 with Radicle
|
// This file is part of radicle-link, distributed under the GPLv3 with Radicle
|
||||||
// Linking Exception. For full terms see the included LICENSE file.
|
// Linking Exception. For full terms see the included LICENSE file.
|
||||||
|
|
||||||
mod author_commit {
|
|
||||||
super::oid_trailer! {AuthorCommitTrailer, "Rad-Author"}
|
|
||||||
}
|
|
||||||
mod resource_identity {
|
mod resource_identity {
|
||||||
super::oid_trailer! {ResourceCommitTrailer, "Rad-Resource"}
|
super::oid_trailer! {ResourceCommitTrailer, "Rad-Resource"}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod error {
|
pub mod error {
|
||||||
pub use super::author_commit::Error as InvalidAuthorTrailer;
|
|
||||||
|
|
||||||
pub use super::resource_identity::Error as InvalidResourceTrailer;
|
pub use super::resource_identity::Error as InvalidResourceTrailer;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use author_commit::AuthorCommitTrailer;
|
|
||||||
pub use resource_identity::ResourceCommitTrailer;
|
pub use resource_identity::ResourceCommitTrailer;
|
||||||
|
|
||||||
/// A macro for generating boilerplate From and TryFrom impls for trailers which
|
/// A macro for generating boilerplate From and TryFrom impls for trailers which
|
||||||
|
|
|
||||||
|
|
@ -13,48 +13,7 @@ pub use common::*;
|
||||||
use radicle_cob as cob;
|
use radicle_cob as cob;
|
||||||
use radicle_git_ext::Oid;
|
use radicle_git_ext::Oid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{identity::project::Identity, storage::git::Repository};
|
||||||
identity::{project::Identity, Did},
|
|
||||||
node::NodeId,
|
|
||||||
storage::git::Repository,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The `Author` of a [`create`] or [`update`].
|
|
||||||
///
|
|
||||||
/// **Note**: `Author` implements [`identity::Identity`], but since it
|
|
||||||
/// is not content-addressed, the [`identity::Identity::content_id`]
|
|
||||||
/// returns [`git2::Oid::zero`]. This means that if the `author` is
|
|
||||||
/// set in the updates the history entries for those changes will
|
|
||||||
/// contain the zero `Oid` for the `author` field.
|
|
||||||
pub struct Author {
|
|
||||||
did: Did,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Did> for Author {
|
|
||||||
fn from(did: Did) -> Self {
|
|
||||||
Self { did }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NodeId> for Author {
|
|
||||||
fn from(node_id: NodeId) -> Self {
|
|
||||||
Self {
|
|
||||||
did: Did::from(node_id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl identity::Identity for Author {
|
|
||||||
type Identifier = String;
|
|
||||||
|
|
||||||
fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
|
|
||||||
*self.did == *delegation
|
|
||||||
}
|
|
||||||
|
|
||||||
fn content_id(&self) -> Oid {
|
|
||||||
git2::Oid::zero().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new [`CollaborativeObject`].
|
/// Create a new [`CollaborativeObject`].
|
||||||
///
|
///
|
||||||
|
|
@ -62,8 +21,7 @@ impl identity::Identity for Author {
|
||||||
/// stored under.
|
/// stored under.
|
||||||
///
|
///
|
||||||
/// The `signer` is used to cryptographically sign the changes made
|
/// The `signer` is used to cryptographically sign the changes made
|
||||||
/// for this update. **Note** that the public key for the signer must
|
/// for this update.
|
||||||
/// match the key of the `Author` -- if it is set.
|
|
||||||
///
|
///
|
||||||
/// The `project` is used to store its content-address in the history
|
/// The `project` is used to store its content-address in the history
|
||||||
/// of changes for the collaborative object.
|
/// of changes for the collaborative object.
|
||||||
|
|
@ -74,7 +32,7 @@ pub fn create<G>(
|
||||||
repository: &Repository,
|
repository: &Repository,
|
||||||
signer: &G,
|
signer: &G,
|
||||||
project: &Identity<Oid>,
|
project: &Identity<Oid>,
|
||||||
args: Create<Author>,
|
args: Create,
|
||||||
) -> Result<CollaborativeObject, error::Create>
|
) -> Result<CollaborativeObject, error::Create>
|
||||||
where
|
where
|
||||||
G: crypto::Signer,
|
G: crypto::Signer,
|
||||||
|
|
@ -118,8 +76,7 @@ pub fn list(
|
||||||
/// stored under.
|
/// stored under.
|
||||||
///
|
///
|
||||||
/// The `signer` is used to cryptographically sign the changes made
|
/// The `signer` is used to cryptographically sign the changes made
|
||||||
/// for this update. **Note** that the public key for the signer must
|
/// for this update.
|
||||||
/// match the key of the `Author` -- if it is set.
|
|
||||||
///
|
///
|
||||||
/// The `project` is used to store its content-address in the history
|
/// The `project` is used to store its content-address in the history
|
||||||
/// of changes for the collaborative object.
|
/// of changes for the collaborative object.
|
||||||
|
|
@ -130,7 +87,7 @@ pub fn update<G>(
|
||||||
repository: &Repository,
|
repository: &Repository,
|
||||||
signer: &G,
|
signer: &G,
|
||||||
project: &Identity<Oid>,
|
project: &Identity<Oid>,
|
||||||
args: Update<Author>,
|
args: Update,
|
||||||
) -> Result<CollaborativeObject, error::Update>
|
) -> Result<CollaborativeObject, error::Update>
|
||||||
where
|
where
|
||||||
G: crypto::Signer,
|
G: crypto::Signer,
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,6 @@ where
|
||||||
signer,
|
signer,
|
||||||
&self.project,
|
&self.project,
|
||||||
Update {
|
Update {
|
||||||
author: Some(cob::Author::from(*signer.public_key())),
|
|
||||||
object_id,
|
object_id,
|
||||||
history_type: HISTORY_TYPE.to_owned(),
|
history_type: HISTORY_TYPE.to_owned(),
|
||||||
typename: T::type_name().clone(),
|
typename: T::type_name().clone(),
|
||||||
|
|
@ -127,7 +126,6 @@ where
|
||||||
signer,
|
signer,
|
||||||
&self.project,
|
&self.project,
|
||||||
Create {
|
Create {
|
||||||
author: Some(cob::Author::from(*signer.public_key())),
|
|
||||||
history_type: HISTORY_TYPE.to_owned(),
|
history_type: HISTORY_TYPE.to_owned(),
|
||||||
typename: T::type_name().clone(),
|
typename: T::type_name().clone(),
|
||||||
message: message.to_owned(),
|
message: message.to_owned(),
|
||||||
|
|
|
||||||
|
|
@ -406,10 +406,6 @@ pub struct Identity<I> {
|
||||||
impl radicle_cob::identity::Identity for Identity<Oid> {
|
impl radicle_cob::identity::Identity for Identity<Oid> {
|
||||||
type Identifier = Oid;
|
type Identifier = Oid;
|
||||||
|
|
||||||
fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
|
|
||||||
self.doc.delegates.iter().any(|d| d.matches(delegation))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn content_id(&self) -> Oid {
|
fn content_id(&self) -> Oid {
|
||||||
self.current
|
self.current
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -681,13 +681,11 @@ impl change::Storage for Repository {
|
||||||
type LoadError = <git2::Repository as change::Storage>::LoadError;
|
type LoadError = <git2::Repository as change::Storage>::LoadError;
|
||||||
|
|
||||||
type ObjectId = <git2::Repository as change::Storage>::ObjectId;
|
type ObjectId = <git2::Repository as change::Storage>::ObjectId;
|
||||||
type Author = <git2::Repository as change::Storage>::Author;
|
|
||||||
type Resource = <git2::Repository as change::Storage>::Resource;
|
type Resource = <git2::Repository as change::Storage>::Resource;
|
||||||
type Signatures = <git2::Repository as change::Storage>::Signatures;
|
type Signatures = <git2::Repository as change::Storage>::Signatures;
|
||||||
|
|
||||||
fn create<Signer>(
|
fn create<Signer>(
|
||||||
&self,
|
&self,
|
||||||
author: Option<Self::Author>,
|
|
||||||
authority: Self::Resource,
|
authority: Self::Resource,
|
||||||
signer: &Signer,
|
signer: &Signer,
|
||||||
spec: change::Create<Self::ObjectId>,
|
spec: change::Create<Self::ObjectId>,
|
||||||
|
|
@ -695,7 +693,7 @@ impl change::Storage for Repository {
|
||||||
where
|
where
|
||||||
Signer: crypto::Signer,
|
Signer: crypto::Signer,
|
||||||
{
|
{
|
||||||
self.backend.create(author, authority, signer, spec)
|
self.backend.create(authority, signer, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(&self, id: Self::ObjectId) -> Result<cob::Change, Self::LoadError> {
|
fn load(&self, id: Self::ObjectId) -> Result<cob::Change, Self::LoadError> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue