cob: Don't require a local fork to exist

Before this change, the identity document would be loaded for the
COB signer. This meant that a fork would be needed for that signer.

After this change, it's no longer necessary, since the identity doc
head is computed from available remotes.

While making those changes, it was also apparent that some of the
identity-related functions had bad names and error types, this
was fixed as well.
This commit is contained in:
Alexis Sellier 2023-03-01 15:40:30 +01:00
parent 2a687502c5
commit 5f59493c70
No known key found for this signature in database
22 changed files with 127 additions and 177 deletions

View File

@ -94,7 +94,7 @@ pub fn execute(options: Options, profile: &Profile) -> anyhow::Result<PathBuf> {
let remote = options.remote.unwrap_or(*profile.id());
let doc = storage
.repository(id)?
.identity_of(&remote)
.identity_doc_of(&remote)
.context("project could not be found in local storage")?;
let payload = doc.project()?;
let path = PathBuf::from(payload.name());

View File

@ -7,14 +7,14 @@ use anyhow::anyhow;
use thiserror::Error;
use radicle::git::raw;
use radicle::identity::doc;
use radicle::identity::doc::{DocError, Id};
use radicle::identity::{doc, IdentityError};
use radicle::node;
use radicle::node::{FetchResult, Handle as _, Node};
use radicle::prelude::*;
use radicle::rad;
use radicle::storage;
use radicle::storage::git::{ProjectError, Storage};
use radicle::storage::git::Storage;
use crate::commands::rad_checkout as checkout;
use crate::project;
@ -151,7 +151,7 @@ pub enum CloneError {
#[error("payload: {0}")]
Payload(#[from] doc::PayloadError),
#[error("project error: {0}")]
Project(#[from] ProjectError),
Identity(#[from] IdentityError),
#[error("no seeds found for {0}")]
NotFound(Id),
}
@ -223,7 +223,7 @@ pub fn clone<G: Signer>(
}
}
let doc = repository.identity_of(&me)?;
let doc = repository.identity_doc_of(&me)?;
let proj = doc.project()?;
let path = Path::new(proj.name());

View File

@ -137,7 +137,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let signer = term::signer(&profile)?;
let repository = profile.storage.repository(id)?;
let _project = repository
.identity_of(profile.id())
.identity_doc_of(profile.id())
.context(format!("couldn't load project {id} from local state"))?;
let repository = profile.storage.repository(id)?;
let mut patches = Patches::open(*profile.id(), &repository)?;

View File

@ -137,7 +137,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
let signer = term::signer(&profile)?;
let repository = profile.storage.repository(id)?;
let _project = repository
.identity_of(profile.id())
.identity_doc_of(profile.id())
.context(format!("couldn't load project {id} from local state"))?;
let mut patches = Patches::open(*profile.id(), &repository)?;

View File

@ -1,19 +0,0 @@
// Copyright © 2022 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;
/// An [`Identity`] represents a content addressed identity
/// (i.e. expected to be stored in a git backend).
///
/// It should have a unique, stable, content addressable identifier.
pub trait Identity {
type Identifier;
/// Provide the content address for the given identity. This is
/// expected to be the latest address for the identity at the time
/// of use.
fn content_id(&self) -> Oid;
}

View File

@ -46,10 +46,6 @@
//! represents the type of resource the collaborative objects are
//! relating to, for example a software project.
//!
//! This `Resource` must implement [`identity::Identity`] to allow the
//! internal logic to reference the resource's content-address in
//! `git` as well as the stable identifier used for the resource.
//!
//! ## History Traversal
//!
//! The [`History`] of a [`CollaborativeObject`] -- accessed via
@ -89,8 +85,6 @@ mod trailers;
pub mod change;
pub use change::Change;
pub mod identity;
pub mod history;
pub use history::{Contents, Entry, History};

View File

@ -8,7 +8,7 @@ use std::collections::BTreeSet;
use git_ext::Oid;
use crate::change::store::Manifest;
use crate::{change, identity::Identity, History, ObjectId, TypeName};
use crate::{change, History, ObjectId, TypeName};
pub mod error;

View File

@ -51,21 +51,20 @@ impl Create {
///
/// The `args` are the metadata for this [`CollaborativeObject`]. See
/// [`Create`] for further information.
pub fn create<S, G, Resource>(
pub fn create<S, G>(
storage: &S,
signer: &G,
resource: &Resource,
resource: Oid,
identifier: &S::Identifier,
args: Create,
) -> Result<CollaborativeObject, error::Create>
where
S: Store,
G: crypto::Signer,
Resource: Identity,
{
let Create { ref typename, .. } = &args;
let init_change = storage
.store(resource.content_id(), signer, args.template())
.store(resource, signer, args.template())
.map_err(error::Create::from)?;
let object_id = init_change.id().into();
@ -76,7 +75,7 @@ where
let history = History::new_from_root(
*init_change.id(),
init_change.signature.key,
resource.content_id(),
resource,
init_change.contents,
init_change.timestamp,
);

View File

@ -3,12 +3,10 @@
// 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 crate::{
change, change_graph::ChangeGraph, identity::Identity, CollaborativeObject, ObjectId, Store,
TypeName,
};
use crate::{change, change_graph::ChangeGraph, CollaborativeObject, ObjectId, Store, TypeName};
use super::error;
@ -44,17 +42,16 @@ pub struct Update {
///
/// The `args` are the metadata for this [`CollaborativeObject`]
/// udpate. See [`Update`] for further information.
pub fn update<S, G, Resource>(
pub fn update<S, G>(
storage: &S,
signer: &G,
resource: &Resource,
resource: Oid,
identifier: &S::Identifier,
args: Update,
) -> Result<CollaborativeObject, error::Update>
where
S: Store,
G: crypto::Signer,
Resource: Identity,
{
let Update {
ref typename,
@ -73,7 +70,7 @@ where
.ok_or(error::Update::NoSuchObject)?;
let change = storage.store(
resource.content_id(),
resource,
signer,
change::Template {
tips: object.tips().iter().cloned().collect(),

View File

@ -1,10 +1,9 @@
use git_ext::Oid;
use serde::{Deserialize, Serialize};
use crate::identity::Identity;
use crate::test::storage::{self, Storage};
use super::{Name, Urn};
use super::Name;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Person {
@ -57,11 +56,3 @@ impl Person {
&self.payload.name
}
}
impl Identity for Person {
type Identifier = Urn;
fn content_id(&self) -> Oid {
self.content_id
}
}

View File

@ -3,7 +3,6 @@ use std::collections::BTreeSet;
use git_ext::Oid;
use serde::{Deserialize, Serialize};
use crate::identity::Identity;
use crate::test;
use crate::test::storage::{self, Storage};
@ -74,11 +73,3 @@ impl Project {
&self.payload.name
}
}
impl Identity for RemoteProject {
type Identifier = Urn;
fn content_id(&self) -> Oid {
self.project.content_id
}
}

View File

@ -26,7 +26,7 @@ fn roundtrip() {
let cob = create(
&storage,
&signer,
&proj,
proj.project.content_id,
&proj.identifier(),
Create {
history_type: "test".to_string(),
@ -58,7 +58,7 @@ fn list_cobs() {
let issue_1 = create(
&storage,
&signer,
&proj,
proj.project.content_id,
&proj.identifier(),
Create {
history_type: "test".to_string(),
@ -72,7 +72,7 @@ fn list_cobs() {
let issue_2 = create(
&storage,
&signer,
&proj,
proj.project.content_id,
&proj.identifier(),
Create {
history_type: "test".to_string(),
@ -106,7 +106,7 @@ fn update_cob() {
let cob = create(
&storage,
&signer,
&proj,
proj.project.content_id,
&proj.identifier(),
Create {
history_type: "test".to_string(),
@ -124,7 +124,7 @@ fn update_cob() {
let updated = update(
&storage,
&signer,
&proj,
proj.project.content_id,
&proj.identifier(),
Update {
changes: nonempty!(b"issue 1".to_vec()),
@ -164,7 +164,7 @@ fn traverse_cobs() {
let cob = create(
&storage,
&terry_signer,
&terry_proj,
terry_proj.project.content_id,
&terry_proj.identifier(),
Create {
contents: nonempty!(b"issue 1".to_vec()),
@ -186,7 +186,7 @@ fn traverse_cobs() {
let updated = update(
&storage,
&neil_signer,
&neil_proj,
neil_proj.project.content_id,
&neil_proj.identifier(),
Update {
changes: nonempty!(b"issue 2".to_vec()),

View File

@ -38,9 +38,9 @@ pub enum Error {
#[error(transparent)]
CobStore(#[from] radicle::cob::store::Error),
/// Git project error.
/// Identity error.
#[error(transparent)]
GitProject(#[from] radicle::storage::git::ProjectError),
Identity(#[from] radicle::identity::IdentityError),
/// Project doc error.
#[error(transparent)]

View File

@ -25,6 +25,7 @@ use crate::address::AddressBook;
use crate::clock::Timestamp;
use crate::crypto;
use crate::crypto::{Signer, Verified};
use crate::identity::IdentityError;
use crate::identity::{Doc, Id};
use crate::node;
use crate::node::{Address, Features, FetchResult, Seed, Seeds};
@ -1277,8 +1278,8 @@ pub trait ServiceState {
fn sessions(&self) -> &Sessions;
/// Get the current inventory.
fn inventory(&self) -> Result<Inventory, storage::Error>;
/// Get a project from storage, using the local node's key.
fn get(&self, proj: Id) -> Result<Option<Doc<Verified>>, storage::ProjectError>;
/// Get a repository from storage, using the local node's key.
fn get(&self, proj: Id) -> Result<Option<Doc<Verified>>, IdentityError>;
/// Get the clock.
fn clock(&self) -> &LocalTime;
/// Get the clock mutably.
@ -1303,7 +1304,7 @@ where
self.storage.inventory()
}
fn get(&self, proj: Id) -> Result<Option<Doc<Verified>>, storage::ProjectError> {
fn get(&self, proj: Id) -> Result<Option<Doc<Verified>>, IdentityError> {
self.storage.get(&self.node_id(), proj)
}
@ -1385,7 +1386,7 @@ pub enum LookupError {
#[error(transparent)]
Routing(#[from] routing::Error),
#[error(transparent)]
Project(#[from] storage::ProjectError),
Identity(#[from] IdentityError),
}
/// Information on a peer, that we may or may not be connected to.

View File

@ -8,7 +8,7 @@ use netservices::tunnel::Tunnel;
use netservices::{NetSession, SplitIo};
use radicle::crypto::Signer;
use radicle::identity::Id;
use radicle::identity::{Id, IdentityError};
use radicle::storage::{Namespaces, ReadRepository, RefUpdate, WriteRepository, WriteStorage};
use radicle::{git, Storage};
use reactor::poller::popol;
@ -46,7 +46,7 @@ pub enum FetchError {
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Project(#[from] storage::ProjectError),
Identity(#[from] IdentityError),
}
impl FetchError {

View File

@ -11,8 +11,8 @@ pub mod test;
pub use cob::{create, get, list, remove, update};
pub use cob::{
history::entry::EntryBlob, identity::Identity, object::collaboration::error,
CollaborativeObject, Contents, Create, Entry, History, ObjectId, TypeName, Update,
history::entry::EntryBlob, object::collaboration::error, CollaborativeObject, Contents, Create,
Entry, History, ObjectId, TypeName, Update,
};
pub use common::*;
pub use op::{ActorId, Op, OpId};

View File

@ -10,17 +10,15 @@ use rand::rngs::StdRng;
use rand::{RngCore as _, SeedableRng};
use serde::{Deserialize, Serialize};
use crate::cob;
use crate::cob::common::Author;
use crate::cob::op::{Nonce, Op, OpId, Ops};
use crate::cob::CollaborativeObject;
use crate::cob::{ActorId, Create, History, ObjectId, TypeName, Update};
use crate::crypto::PublicKey;
use crate::git;
use crate::identity;
use crate::identity::Identity;
use crate::prelude::*;
use crate::storage::git as storage;
use crate::{cob, identity};
/// History type for standard radicle COBs.
pub const HISTORY_TYPE: &str = "radicle";
@ -91,7 +89,7 @@ pub enum Error {
/// Storage for collaborative objects of a specific type `T` in a single repository.
pub struct Store<'a, T> {
whoami: PublicKey,
identity: Identity<git::Oid>,
parent: git::Oid,
raw: &'a storage::Repository,
witness: PhantomData<T>,
rng: StdRng,
@ -107,10 +105,10 @@ impl<'a, T> Store<'a, T> {
/// Open a new generic store.
pub fn open(whoami: PublicKey, store: &'a storage::Repository) -> Result<Self, Error> {
let rng = rng::std();
let identity = Identity::load(&whoami, store)?;
let identity = store.identity()?;
Ok(Self {
identity,
parent: identity.current,
whoami,
raw: store,
witness: PhantomData,
@ -151,7 +149,7 @@ where
cob::update(
self.raw,
signer,
&self.identity,
self.parent,
signer.public_key(),
Update {
object_id,
@ -175,7 +173,7 @@ where
let cob = cob::create(
self.raw,
signer,
&self.identity,
self.parent,
signer.public_key(),
Create {
history_type: HISTORY_TYPE.to_owned(),

View File

@ -10,11 +10,12 @@ use thiserror::Error;
use crate::crypto;
use crate::crypto::{Signature, Verified};
use crate::git;
use crate::storage::{ReadRepository, RemoteId};
use crate::storage;
use crate::storage::{refs, ReadRepository, RemoteId};
pub use crypto::PublicKey;
pub use did::Did;
pub use doc::{Doc, Id, IdError};
pub use doc::{Doc, Id, IdError, PayloadError};
pub use project::Project;
/// Untrusted, well-formed input.
@ -30,18 +31,39 @@ pub enum IdentityError {
Git(#[from] git2::Error),
#[error("git: {0}")]
GitExt(#[from] git::Error),
#[error("identity branches diverge from each other")]
BranchesDiverge,
#[error("root hash `{0}` does not match project")]
MismatchedRoot(Oid),
#[error("the identity branch is missing")]
MissingBranch,
#[error("the document root is missing")]
MissingRoot,
#[error("root commit is missing one or more delegate signatures")]
MissingRootSignatures,
#[error(transparent)]
Payload(#[from] PayloadError),
#[error("commit signature for {0} is invalid: {1}")]
InvalidSignature(PublicKey, crypto::Error),
#[error("threshold not reached: {0} signatures for a threshold of {1}")]
ThresholdNotReached(usize, usize),
#[error("identity document error: {0}")]
Doc(#[from] doc::DocError),
#[error(transparent)]
Refs(#[from] refs::Error),
#[error(transparent)]
Storage(#[from] storage::Error),
}
impl IdentityError {
/// Whether this error is caused by something not being found.
pub fn is_not_found(&self) -> bool {
match self {
Self::Doc(doc) => doc.is_not_found(),
Self::Refs(refs) => refs.is_not_found(),
_ => false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
@ -62,14 +84,6 @@ pub struct Identity<I> {
pub signatures: HashMap<PublicKey, Signature>,
}
impl radicle_cob::identity::Identity for Identity<Oid> {
type Identifier = Oid;
fn content_id(&self) -> Oid {
self.current
}
}
impl Identity<Oid> {
pub fn verified(self, id: doc::Id) -> Result<Identity<doc::Id>, IdentityError> {
// The root hash must be equal to the id.
@ -94,6 +108,11 @@ impl Identity<Untrusted> {
repo: &R,
) -> Result<Identity<Oid>, IdentityError> {
let head = Doc::<Untrusted>::head(remote, repo)?;
Self::load_at(head, repo)
}
pub fn load_at<R: ReadRepository>(head: Oid, repo: &R) -> Result<Identity<Oid>, IdentityError> {
let mut history = repo.revwalk(head)?.collect::<Vec<_>>();
// Retrieve root document.

View File

@ -8,11 +8,11 @@ use thiserror::Error;
use crate::crypto::{Signer, Verified};
use crate::git;
use crate::identity::doc;
use crate::identity::doc::{DocError, Id};
use crate::identity::project::Project;
use crate::identity::{doc, IdentityError};
use crate::storage::git::transport;
use crate::storage::git::{ProjectError, Repository, Storage};
use crate::storage::git::{Repository, Storage};
use crate::storage::refs::SignedRefs;
use crate::storage::WriteRepository;
use crate::storage::{BranchName, ReadRepository as _, RemoteId};
@ -26,7 +26,7 @@ pub enum InitError {
#[error("doc: {0}")]
Doc(#[from] DocError),
#[error("project: {0}")]
Project(#[from] storage::git::ProjectError),
Identity(#[from] IdentityError),
#[error("project payload: {0}")]
ProjectPayload(String),
#[error("git: {0}")]
@ -102,7 +102,7 @@ pub enum ForkError {
#[error("project `{0}` was not found in storage")]
NotFound(Id),
#[error("project identity error: {0}")]
InvalidIdentity(#[from] storage::git::ProjectError),
InvalidIdentity(#[from] IdentityError),
#[error("project identity document error: {0}")]
Doc(#[from] DocError),
#[error("git: invalid reference")]
@ -199,7 +199,7 @@ pub enum CheckoutError {
#[error("project `{0}` was not found in storage")]
NotFound(Id),
#[error("project error: {0}")]
Project(#[from] ProjectError),
Identity(#[from] IdentityError),
}
/// Checkout a project from storage as a working copy.

View File

@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
use thiserror::Error;
use crypto::{PublicKey, Signer, Unverified, Verified};
pub use git::{ProjectError, VerifyError};
pub use git::VerifyError;
pub use radicle_git_ext::Oid;
use crate::collections::HashMap;
@ -18,7 +18,7 @@ use crate::git::ext as git_ext;
use crate::git::{Qualified, RefError, RefString};
use crate::identity;
use crate::identity::doc::DocError;
use crate::identity::{Id, IdError};
use crate::identity::{Id, IdError, IdentityError};
use crate::storage::refs::Refs;
use self::refs::SignedRefs;
@ -98,7 +98,7 @@ pub enum FetchError {
Storage(#[from] Error),
// TODO: This should wrap a more specific error.
#[error("repository head: {0}")]
SetHead(#[from] ProjectError),
SetHead(#[from] IdentityError),
}
pub type RemoteId = PublicKey;
@ -269,9 +269,9 @@ pub trait ReadStorage {
&self,
remote: &RemoteId,
rid: Id,
) -> Result<Option<identity::Doc<Verified>>, ProjectError>;
) -> Result<Option<identity::Doc<Verified>>, IdentityError>;
/// Check whether storage contains a repository.
fn contains(&self, rid: &Id) -> Result<bool, ProjectError>;
fn contains(&self, rid: &Id) -> Result<bool, IdentityError>;
/// Get the inventory of repositories hosted under this storage.
fn inventory(&self) -> Result<Inventory, Error>;
/// Open or create a read-only repository.
@ -314,14 +314,14 @@ pub trait ReadRepository {
/// head using [`ReadRepository::canonical_head`].
///
/// Returns the [`Oid`] as well as the qualified reference name.
fn head(&self) -> Result<(Qualified, Oid), ProjectError>;
fn head(&self) -> Result<(Qualified, Oid), IdentityError>;
/// Compute the canonical head of this repository.
///
/// Ignores any existing `HEAD` reference.
///
/// Returns the [`Oid`] as well as the qualified reference name.
fn canonical_head(&self) -> Result<(Qualified, Oid), ProjectError>;
fn canonical_head(&self) -> Result<(Qualified, Oid), IdentityError>;
/// Get the `reference` for the given `remote`.
///
@ -357,14 +357,14 @@ pub trait ReadRepository {
fn remotes(&self) -> Result<Remotes<Verified>, refs::Error>;
/// Get the repository's identity document.
fn identity_doc(&self) -> Result<(Oid, identity::Doc<Unverified>), ProjectError>;
fn identity_doc(&self) -> Result<(Oid, identity::Doc<Unverified>), IdentityError>;
}
/// Allows read-write access to a repository.
pub trait WriteRepository: ReadRepository {
/// Set the repository head to the canonical branch.
/// This computes the head based on the delegate set.
fn set_head(&self) -> Result<Oid, ProjectError>;
fn set_head(&self) -> Result<Oid, IdentityError>;
/// Sign the repository's refs under the `refs/rad/sigrefs` branch.
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, Error>;
/// Get the underlying git repository.
@ -386,7 +386,7 @@ where
self.deref().path_of(rid)
}
fn contains(&self, rid: &Id) -> Result<bool, ProjectError> {
fn contains(&self, rid: &Id) -> Result<bool, IdentityError> {
self.deref().contains(rid)
}
@ -398,7 +398,7 @@ where
&self,
remote: &RemoteId,
proj: Id,
) -> Result<Option<identity::Doc<Verified>>, ProjectError> {
) -> Result<Option<identity::Doc<Verified>>, IdentityError> {
self.deref().get(remote, proj)
}

View File

@ -11,7 +11,7 @@ use once_cell::sync::Lazy;
use crate::git;
use crate::identity;
use crate::identity::{doc, Doc, Id};
use crate::identity::{Doc, Id};
use crate::identity::{Identity, IdentityError, Project};
use crate::storage::refs;
use crate::storage::refs::{Refs, SignedRefs};
@ -58,37 +58,6 @@ impl<'a> TryFrom<git2::Reference<'a>> for Ref {
}
}
// TODO: Is this is the wrong place for this type?
#[derive(Error, Debug)]
pub enum ProjectError {
#[error("identity branches diverge from each other")]
BranchesDiverge,
#[error("identity branches missing")]
MissingHeads,
#[error("storage error: {0}")]
Storage(#[from] Error),
#[error("identity document error: {0}")]
Doc(#[from] doc::DocError),
#[error("payload error: {0}")]
Payload(#[from] doc::PayloadError),
#[error("git: {0}")]
Git(#[from] git2::Error),
#[error("git: {0}")]
GitExt(#[from] git::Error),
#[error("refs: {0}")]
Refs(#[from] refs::Error),
}
impl ProjectError {
/// Whether this error is caused by the project not being found.
pub fn is_not_found(&self) -> bool {
match self {
Self::Doc(doc) => doc.is_not_found(),
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub struct Storage {
path: PathBuf,
@ -105,7 +74,7 @@ impl ReadStorage for Storage {
paths::repository(&self, rid)
}
fn contains(&self, rid: &Id) -> Result<bool, ProjectError> {
fn contains(&self, rid: &Id) -> Result<bool, IdentityError> {
if paths::repository(&self, rid).exists() {
let _ = self.repository(*rid)?.head()?;
return Ok(true);
@ -113,13 +82,13 @@ impl ReadStorage for Storage {
Ok(false)
}
fn get(&self, remote: &RemoteId, proj: Id) -> Result<Option<Doc<Verified>>, ProjectError> {
fn get(&self, remote: &RemoteId, proj: Id) -> Result<Option<Doc<Verified>>, IdentityError> {
let repo = match self.repository(proj) {
Ok(doc) => doc,
Err(e) if e.is_not_found() => return Ok(None),
Err(e) => return Err(e.into()),
};
match repo.identity_of(remote) {
match repo.identity_doc_of(remote) {
Ok(doc) => Ok(Some(doc)),
Err(e) if e.is_not_found() => Ok(None),
Err(e) => Err(e),
@ -300,18 +269,24 @@ impl Repository {
Ok(refs)
}
pub fn identity(&self, remote: &RemoteId) -> Result<Identity<Oid>, IdentityError> {
pub fn identity_of(&self, remote: &RemoteId) -> Result<Identity<Oid>, IdentityError> {
Identity::load(remote, self)
}
pub fn project_of(&self, remote: &RemoteId) -> Result<Project, ProjectError> {
let doc = self.identity_of(remote)?;
pub fn identity(&self) -> Result<Identity<Oid>, IdentityError> {
let head = self.identity_head()?;
Identity::load_at(head, self)
}
pub fn project_of(&self, remote: &RemoteId) -> Result<Project, IdentityError> {
let doc = self.identity_doc_of(remote)?;
let proj = doc.project()?;
Ok(proj)
}
pub fn identity_of(&self, remote: &RemoteId) -> Result<Doc<Verified>, ProjectError> {
pub fn identity_doc_of(&self, remote: &RemoteId) -> Result<Doc<Verified>, IdentityError> {
let (doc, _) = identity::Doc::load(remote, self)?;
let verified = doc.verified()?;
@ -319,8 +294,18 @@ impl Repository {
}
/// Return the canonical identity [`git::Oid`] and document.
pub fn identity_doc(&self) -> Result<(Oid, identity::Doc<Unverified>), ProjectError> {
pub fn identity_doc(&self) -> Result<(Oid, identity::Doc<Unverified>), IdentityError> {
let head = self.identity_head()?;
Doc::<Unverified>::load_at(head, self)
.map(|(doc, _)| (head, doc))
.map_err(IdentityError::from)
}
/// Return the canonical identity branch head.
pub fn identity_head(&self) -> Result<Oid, IdentityError> {
let mut heads = Vec::new();
for remote in self.remote_ids()? {
let remote = remote?;
let oid = Doc::<Unverified>::head(&remote, self)?;
@ -328,7 +313,7 @@ impl Repository {
heads.push(oid.into());
}
// Keep track of the longest identity branch.
let mut longest = heads.pop().ok_or(ProjectError::MissingHeads)?;
let mut longest = heads.pop().ok_or(IdentityError::MissingBranch)?;
for head in &heads {
let base = self.raw().merge_base(*head, longest)?;
@ -360,13 +345,10 @@ impl Repository {
// o (base)
// |
//
return Err(ProjectError::BranchesDiverge);
return Err(IdentityError::BranchesDiverge);
}
}
Doc::<Unverified>::load_at(longest.into(), self)
.map(|(doc, _)| (longest.into(), doc))
.map_err(ProjectError::from)
Ok(longest.into())
}
pub fn remote_ids(
@ -484,7 +466,7 @@ impl ReadRepository for Repository {
return Err(VerifyError::MissingRef(remote, name));
}
// Verify identity history of remote.
self.identity(&remote)?.verified(self.id)?;
self.identity_of(&remote)?.verified(self.id)?;
}
Ok(())
@ -554,11 +536,11 @@ impl ReadRepository for Repository {
Ok(Remotes::from_iter(remotes))
}
fn identity_doc(&self) -> Result<(Oid, identity::Doc<Unverified>), ProjectError> {
fn identity_doc(&self) -> Result<(Oid, identity::Doc<Unverified>), IdentityError> {
Repository::identity_doc(self)
}
fn head(&self) -> Result<(Qualified, Oid), ProjectError> {
fn head(&self) -> Result<(Qualified, Oid), IdentityError> {
// If `HEAD` is already set locally, just return that.
if let Ok(head) = self.backend.head() {
if let Ok((name, oid)) = git::refs::qualified_from(&head) {
@ -568,7 +550,7 @@ impl ReadRepository for Repository {
self.canonical_head()
}
fn canonical_head(&self) -> Result<(Qualified, Oid), ProjectError> {
fn canonical_head(&self) -> Result<(Qualified, Oid), IdentityError> {
// TODO: In the `fork` function for example, we call Repository::project_identity again,
// This should only be necessary once.
let (_, doc) = self.identity_doc()?;
@ -595,7 +577,7 @@ impl ReadRepository for Repository {
}
impl WriteRepository for Repository {
fn set_head(&self) -> Result<Oid, ProjectError> {
fn set_head(&self) -> Result<Oid, IdentityError> {
let head_ref = refname!("HEAD");
let (branch_ref, head) = self.canonical_head()?;

View File

@ -6,6 +6,7 @@ use radicle_git_ext as git_ext;
use crate::crypto::{Signer, Verified};
use crate::identity::doc::{Doc, Id};
use crate::identity::IdentityError;
pub use crate::storage::*;
@ -42,15 +43,11 @@ impl ReadStorage for MockStorage {
self.path().join(rid.canonical())
}
fn contains(&self, rid: &Id) -> Result<bool, ProjectError> {
fn contains(&self, rid: &Id) -> Result<bool, IdentityError> {
Ok(self.inventory.contains_key(rid))
}
fn get(
&self,
_remote: &RemoteId,
proj: Id,
) -> Result<Option<Doc<Verified>>, git::ProjectError> {
fn get(&self, _remote: &RemoteId, proj: Id) -> Result<Option<Doc<Verified>>, IdentityError> {
Ok(self.inventory.get(&proj).cloned())
}
@ -86,11 +83,11 @@ impl ReadRepository for MockRepository {
Ok(true)
}
fn head(&self) -> Result<(fmt::Qualified, Oid), ProjectError> {
fn head(&self) -> Result<(fmt::Qualified, Oid), IdentityError> {
todo!()
}
fn canonical_head(&self) -> Result<(fmt::Qualified, Oid), ProjectError> {
fn canonical_head(&self) -> Result<(fmt::Qualified, Oid), IdentityError> {
todo!()
}
@ -148,7 +145,7 @@ impl ReadRepository for MockRepository {
fn identity_doc(
&self,
) -> Result<(Oid, crate::identity::Doc<crate::crypto::Unverified>), git::ProjectError> {
) -> Result<(Oid, crate::identity::Doc<crate::crypto::Unverified>), IdentityError> {
todo!()
}
}
@ -158,7 +155,7 @@ impl WriteRepository for MockRepository {
todo!()
}
fn set_head(&self) -> Result<Oid, ProjectError> {
fn set_head(&self) -> Result<Oid, IdentityError> {
todo!()
}