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:
parent
2a687502c5
commit
5f59493c70
|
|
@ -94,7 +94,7 @@ pub fn execute(options: Options, profile: &Profile) -> anyhow::Result<PathBuf> {
|
||||||
let remote = options.remote.unwrap_or(*profile.id());
|
let remote = options.remote.unwrap_or(*profile.id());
|
||||||
let doc = storage
|
let doc = storage
|
||||||
.repository(id)?
|
.repository(id)?
|
||||||
.identity_of(&remote)
|
.identity_doc_of(&remote)
|
||||||
.context("project could not be found in local storage")?;
|
.context("project could not be found in local storage")?;
|
||||||
let payload = doc.project()?;
|
let payload = doc.project()?;
|
||||||
let path = PathBuf::from(payload.name());
|
let path = PathBuf::from(payload.name());
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,14 @@ use anyhow::anyhow;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use radicle::git::raw;
|
use radicle::git::raw;
|
||||||
use radicle::identity::doc;
|
|
||||||
use radicle::identity::doc::{DocError, Id};
|
use radicle::identity::doc::{DocError, Id};
|
||||||
|
use radicle::identity::{doc, IdentityError};
|
||||||
use radicle::node;
|
use radicle::node;
|
||||||
use radicle::node::{FetchResult, Handle as _, Node};
|
use radicle::node::{FetchResult, Handle as _, Node};
|
||||||
use radicle::prelude::*;
|
use radicle::prelude::*;
|
||||||
use radicle::rad;
|
use radicle::rad;
|
||||||
use radicle::storage;
|
use radicle::storage;
|
||||||
use radicle::storage::git::{ProjectError, Storage};
|
use radicle::storage::git::Storage;
|
||||||
|
|
||||||
use crate::commands::rad_checkout as checkout;
|
use crate::commands::rad_checkout as checkout;
|
||||||
use crate::project;
|
use crate::project;
|
||||||
|
|
@ -151,7 +151,7 @@ pub enum CloneError {
|
||||||
#[error("payload: {0}")]
|
#[error("payload: {0}")]
|
||||||
Payload(#[from] doc::PayloadError),
|
Payload(#[from] doc::PayloadError),
|
||||||
#[error("project error: {0}")]
|
#[error("project error: {0}")]
|
||||||
Project(#[from] ProjectError),
|
Identity(#[from] IdentityError),
|
||||||
#[error("no seeds found for {0}")]
|
#[error("no seeds found for {0}")]
|
||||||
NotFound(Id),
|
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 proj = doc.project()?;
|
||||||
let path = Path::new(proj.name());
|
let path = Path::new(proj.name());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let signer = term::signer(&profile)?;
|
let signer = term::signer(&profile)?;
|
||||||
let repository = profile.storage.repository(id)?;
|
let repository = profile.storage.repository(id)?;
|
||||||
let _project = repository
|
let _project = repository
|
||||||
.identity_of(profile.id())
|
.identity_doc_of(profile.id())
|
||||||
.context(format!("couldn't load project {id} from local state"))?;
|
.context(format!("couldn't load project {id} from local state"))?;
|
||||||
let repository = profile.storage.repository(id)?;
|
let repository = profile.storage.repository(id)?;
|
||||||
let mut patches = Patches::open(*profile.id(), &repository)?;
|
let mut patches = Patches::open(*profile.id(), &repository)?;
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let signer = term::signer(&profile)?;
|
let signer = term::signer(&profile)?;
|
||||||
let repository = profile.storage.repository(id)?;
|
let repository = profile.storage.repository(id)?;
|
||||||
let _project = repository
|
let _project = repository
|
||||||
.identity_of(profile.id())
|
.identity_doc_of(profile.id())
|
||||||
.context(format!("couldn't load project {id} from local state"))?;
|
.context(format!("couldn't load project {id} from local state"))?;
|
||||||
let mut patches = Patches::open(*profile.id(), &repository)?;
|
let mut patches = Patches::open(*profile.id(), &repository)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -46,10 +46,6 @@
|
||||||
//! represents the type of resource the collaborative objects are
|
//! represents the type of resource the collaborative objects are
|
||||||
//! relating to, for example a software project.
|
//! 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
|
//! ## History Traversal
|
||||||
//!
|
//!
|
||||||
//! The [`History`] of a [`CollaborativeObject`] -- accessed via
|
//! The [`History`] of a [`CollaborativeObject`] -- accessed via
|
||||||
|
|
@ -89,8 +85,6 @@ mod trailers;
|
||||||
pub mod change;
|
pub mod change;
|
||||||
pub use change::Change;
|
pub use change::Change;
|
||||||
|
|
||||||
pub mod identity;
|
|
||||||
|
|
||||||
pub mod history;
|
pub mod history;
|
||||||
pub use history::{Contents, Entry, History};
|
pub use history::{Contents, Entry, History};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use std::collections::BTreeSet;
|
||||||
use git_ext::Oid;
|
use git_ext::Oid;
|
||||||
|
|
||||||
use crate::change::store::Manifest;
|
use crate::change::store::Manifest;
|
||||||
use crate::{change, identity::Identity, History, ObjectId, TypeName};
|
use crate::{change, History, ObjectId, TypeName};
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,21 +51,20 @@ impl Create {
|
||||||
///
|
///
|
||||||
/// 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, Resource>(
|
pub fn create<S, G>(
|
||||||
storage: &S,
|
storage: &S,
|
||||||
signer: &G,
|
signer: &G,
|
||||||
resource: &Resource,
|
resource: Oid,
|
||||||
identifier: &S::Identifier,
|
identifier: &S::Identifier,
|
||||||
args: Create,
|
args: Create,
|
||||||
) -> Result<CollaborativeObject, error::Create>
|
) -> Result<CollaborativeObject, error::Create>
|
||||||
where
|
where
|
||||||
S: Store,
|
S: Store,
|
||||||
G: crypto::Signer,
|
G: crypto::Signer,
|
||||||
Resource: Identity,
|
|
||||||
{
|
{
|
||||||
let Create { ref typename, .. } = &args;
|
let Create { ref typename, .. } = &args;
|
||||||
let init_change = storage
|
let init_change = storage
|
||||||
.store(resource.content_id(), signer, args.template())
|
.store(resource, signer, args.template())
|
||||||
.map_err(error::Create::from)?;
|
.map_err(error::Create::from)?;
|
||||||
let object_id = init_change.id().into();
|
let object_id = init_change.id().into();
|
||||||
|
|
||||||
|
|
@ -76,7 +75,7 @@ where
|
||||||
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,
|
||||||
resource.content_id(),
|
resource,
|
||||||
init_change.contents,
|
init_change.contents,
|
||||||
init_change.timestamp,
|
init_change.timestamp,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,10 @@
|
||||||
// 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.
|
||||||
|
|
||||||
|
use git_ext::Oid;
|
||||||
use nonempty::NonEmpty;
|
use nonempty::NonEmpty;
|
||||||
|
|
||||||
use crate::{
|
use crate::{change, change_graph::ChangeGraph, CollaborativeObject, ObjectId, Store, TypeName};
|
||||||
change, change_graph::ChangeGraph, identity::Identity, CollaborativeObject, ObjectId, Store,
|
|
||||||
TypeName,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::error;
|
use super::error;
|
||||||
|
|
||||||
|
|
@ -44,17 +42,16 @@ pub struct Update {
|
||||||
///
|
///
|
||||||
/// 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>(
|
pub fn update<S, G>(
|
||||||
storage: &S,
|
storage: &S,
|
||||||
signer: &G,
|
signer: &G,
|
||||||
resource: &Resource,
|
resource: Oid,
|
||||||
identifier: &S::Identifier,
|
identifier: &S::Identifier,
|
||||||
args: Update,
|
args: Update,
|
||||||
) -> Result<CollaborativeObject, error::Update>
|
) -> Result<CollaborativeObject, error::Update>
|
||||||
where
|
where
|
||||||
S: Store,
|
S: Store,
|
||||||
G: crypto::Signer,
|
G: crypto::Signer,
|
||||||
Resource: Identity,
|
|
||||||
{
|
{
|
||||||
let Update {
|
let Update {
|
||||||
ref typename,
|
ref typename,
|
||||||
|
|
@ -73,7 +70,7 @@ where
|
||||||
.ok_or(error::Update::NoSuchObject)?;
|
.ok_or(error::Update::NoSuchObject)?;
|
||||||
|
|
||||||
let change = storage.store(
|
let change = storage.store(
|
||||||
resource.content_id(),
|
resource,
|
||||||
signer,
|
signer,
|
||||||
change::Template {
|
change::Template {
|
||||||
tips: object.tips().iter().cloned().collect(),
|
tips: object.tips().iter().cloned().collect(),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use git_ext::Oid;
|
use git_ext::Oid;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::identity::Identity;
|
|
||||||
use crate::test::storage::{self, Storage};
|
use crate::test::storage::{self, Storage};
|
||||||
|
|
||||||
use super::{Name, Urn};
|
use super::Name;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
|
|
@ -57,11 +56,3 @@ impl Person {
|
||||||
&self.payload.name
|
&self.payload.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Identity for Person {
|
|
||||||
type Identifier = Urn;
|
|
||||||
|
|
||||||
fn content_id(&self) -> Oid {
|
|
||||||
self.content_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ use std::collections::BTreeSet;
|
||||||
use git_ext::Oid;
|
use git_ext::Oid;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::identity::Identity;
|
|
||||||
use crate::test;
|
use crate::test;
|
||||||
use crate::test::storage::{self, Storage};
|
use crate::test::storage::{self, Storage};
|
||||||
|
|
||||||
|
|
@ -74,11 +73,3 @@ impl Project {
|
||||||
&self.payload.name
|
&self.payload.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Identity for RemoteProject {
|
|
||||||
type Identifier = Urn;
|
|
||||||
|
|
||||||
fn content_id(&self) -> Oid {
|
|
||||||
self.project.content_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ fn roundtrip() {
|
||||||
let cob = create(
|
let cob = create(
|
||||||
&storage,
|
&storage,
|
||||||
&signer,
|
&signer,
|
||||||
&proj,
|
proj.project.content_id,
|
||||||
&proj.identifier(),
|
&proj.identifier(),
|
||||||
Create {
|
Create {
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
|
|
@ -58,7 +58,7 @@ fn list_cobs() {
|
||||||
let issue_1 = create(
|
let issue_1 = create(
|
||||||
&storage,
|
&storage,
|
||||||
&signer,
|
&signer,
|
||||||
&proj,
|
proj.project.content_id,
|
||||||
&proj.identifier(),
|
&proj.identifier(),
|
||||||
Create {
|
Create {
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
|
|
@ -72,7 +72,7 @@ fn list_cobs() {
|
||||||
let issue_2 = create(
|
let issue_2 = create(
|
||||||
&storage,
|
&storage,
|
||||||
&signer,
|
&signer,
|
||||||
&proj,
|
proj.project.content_id,
|
||||||
&proj.identifier(),
|
&proj.identifier(),
|
||||||
Create {
|
Create {
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
|
|
@ -106,7 +106,7 @@ fn update_cob() {
|
||||||
let cob = create(
|
let cob = create(
|
||||||
&storage,
|
&storage,
|
||||||
&signer,
|
&signer,
|
||||||
&proj,
|
proj.project.content_id,
|
||||||
&proj.identifier(),
|
&proj.identifier(),
|
||||||
Create {
|
Create {
|
||||||
history_type: "test".to_string(),
|
history_type: "test".to_string(),
|
||||||
|
|
@ -124,7 +124,7 @@ fn update_cob() {
|
||||||
let updated = update(
|
let updated = update(
|
||||||
&storage,
|
&storage,
|
||||||
&signer,
|
&signer,
|
||||||
&proj,
|
proj.project.content_id,
|
||||||
&proj.identifier(),
|
&proj.identifier(),
|
||||||
Update {
|
Update {
|
||||||
changes: nonempty!(b"issue 1".to_vec()),
|
changes: nonempty!(b"issue 1".to_vec()),
|
||||||
|
|
@ -164,7 +164,7 @@ fn traverse_cobs() {
|
||||||
let cob = create(
|
let cob = create(
|
||||||
&storage,
|
&storage,
|
||||||
&terry_signer,
|
&terry_signer,
|
||||||
&terry_proj,
|
terry_proj.project.content_id,
|
||||||
&terry_proj.identifier(),
|
&terry_proj.identifier(),
|
||||||
Create {
|
Create {
|
||||||
contents: nonempty!(b"issue 1".to_vec()),
|
contents: nonempty!(b"issue 1".to_vec()),
|
||||||
|
|
@ -186,7 +186,7 @@ fn traverse_cobs() {
|
||||||
let updated = update(
|
let updated = update(
|
||||||
&storage,
|
&storage,
|
||||||
&neil_signer,
|
&neil_signer,
|
||||||
&neil_proj,
|
neil_proj.project.content_id,
|
||||||
&neil_proj.identifier(),
|
&neil_proj.identifier(),
|
||||||
Update {
|
Update {
|
||||||
changes: nonempty!(b"issue 2".to_vec()),
|
changes: nonempty!(b"issue 2".to_vec()),
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,9 @@ pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
CobStore(#[from] radicle::cob::store::Error),
|
CobStore(#[from] radicle::cob::store::Error),
|
||||||
|
|
||||||
/// Git project error.
|
/// Identity error.
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
GitProject(#[from] radicle::storage::git::ProjectError),
|
Identity(#[from] radicle::identity::IdentityError),
|
||||||
|
|
||||||
/// Project doc error.
|
/// Project doc error.
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ use crate::address::AddressBook;
|
||||||
use crate::clock::Timestamp;
|
use crate::clock::Timestamp;
|
||||||
use crate::crypto;
|
use crate::crypto;
|
||||||
use crate::crypto::{Signer, Verified};
|
use crate::crypto::{Signer, Verified};
|
||||||
|
use crate::identity::IdentityError;
|
||||||
use crate::identity::{Doc, Id};
|
use crate::identity::{Doc, Id};
|
||||||
use crate::node;
|
use crate::node;
|
||||||
use crate::node::{Address, Features, FetchResult, Seed, Seeds};
|
use crate::node::{Address, Features, FetchResult, Seed, Seeds};
|
||||||
|
|
@ -1277,8 +1278,8 @@ pub trait ServiceState {
|
||||||
fn sessions(&self) -> &Sessions;
|
fn sessions(&self) -> &Sessions;
|
||||||
/// Get the current inventory.
|
/// Get the current inventory.
|
||||||
fn inventory(&self) -> Result<Inventory, storage::Error>;
|
fn inventory(&self) -> Result<Inventory, storage::Error>;
|
||||||
/// Get a project from storage, using the local node's key.
|
/// Get a repository from storage, using the local node's key.
|
||||||
fn get(&self, proj: Id) -> Result<Option<Doc<Verified>>, storage::ProjectError>;
|
fn get(&self, proj: Id) -> Result<Option<Doc<Verified>>, IdentityError>;
|
||||||
/// Get the clock.
|
/// Get the clock.
|
||||||
fn clock(&self) -> &LocalTime;
|
fn clock(&self) -> &LocalTime;
|
||||||
/// Get the clock mutably.
|
/// Get the clock mutably.
|
||||||
|
|
@ -1303,7 +1304,7 @@ where
|
||||||
self.storage.inventory()
|
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)
|
self.storage.get(&self.node_id(), proj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1385,7 +1386,7 @@ pub enum LookupError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Routing(#[from] routing::Error),
|
Routing(#[from] routing::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Project(#[from] storage::ProjectError),
|
Identity(#[from] IdentityError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information on a peer, that we may or may not be connected to.
|
/// Information on a peer, that we may or may not be connected to.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use netservices::tunnel::Tunnel;
|
||||||
use netservices::{NetSession, SplitIo};
|
use netservices::{NetSession, SplitIo};
|
||||||
|
|
||||||
use radicle::crypto::Signer;
|
use radicle::crypto::Signer;
|
||||||
use radicle::identity::Id;
|
use radicle::identity::{Id, IdentityError};
|
||||||
use radicle::storage::{Namespaces, ReadRepository, RefUpdate, WriteRepository, WriteStorage};
|
use radicle::storage::{Namespaces, ReadRepository, RefUpdate, WriteRepository, WriteStorage};
|
||||||
use radicle::{git, Storage};
|
use radicle::{git, Storage};
|
||||||
use reactor::poller::popol;
|
use reactor::poller::popol;
|
||||||
|
|
@ -46,7 +46,7 @@ pub enum FetchError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] io::Error),
|
Io(#[from] io::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Project(#[from] storage::ProjectError),
|
Identity(#[from] IdentityError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FetchError {
|
impl FetchError {
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ pub mod test;
|
||||||
|
|
||||||
pub use cob::{create, get, list, remove, update};
|
pub use cob::{create, get, list, remove, update};
|
||||||
pub use cob::{
|
pub use cob::{
|
||||||
history::entry::EntryBlob, identity::Identity, object::collaboration::error,
|
history::entry::EntryBlob, object::collaboration::error, CollaborativeObject, Contents, Create,
|
||||||
CollaborativeObject, Contents, Create, Entry, History, ObjectId, TypeName, Update,
|
Entry, History, ObjectId, TypeName, Update,
|
||||||
};
|
};
|
||||||
pub use common::*;
|
pub use common::*;
|
||||||
pub use op::{ActorId, Op, OpId};
|
pub use op::{ActorId, Op, OpId};
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,15 @@ use rand::rngs::StdRng;
|
||||||
use rand::{RngCore as _, SeedableRng};
|
use rand::{RngCore as _, SeedableRng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::cob;
|
|
||||||
use crate::cob::common::Author;
|
use crate::cob::common::Author;
|
||||||
use crate::cob::op::{Nonce, Op, OpId, Ops};
|
use crate::cob::op::{Nonce, Op, OpId, Ops};
|
||||||
use crate::cob::CollaborativeObject;
|
use crate::cob::CollaborativeObject;
|
||||||
use crate::cob::{ActorId, Create, History, ObjectId, TypeName, Update};
|
use crate::cob::{ActorId, Create, History, ObjectId, TypeName, Update};
|
||||||
use crate::crypto::PublicKey;
|
use crate::crypto::PublicKey;
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::identity;
|
|
||||||
use crate::identity::Identity;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::storage::git as storage;
|
use crate::storage::git as storage;
|
||||||
|
use crate::{cob, identity};
|
||||||
|
|
||||||
/// History type for standard radicle COBs.
|
/// History type for standard radicle COBs.
|
||||||
pub const HISTORY_TYPE: &str = "radicle";
|
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.
|
/// Storage for collaborative objects of a specific type `T` in a single repository.
|
||||||
pub struct Store<'a, T> {
|
pub struct Store<'a, T> {
|
||||||
whoami: PublicKey,
|
whoami: PublicKey,
|
||||||
identity: Identity<git::Oid>,
|
parent: git::Oid,
|
||||||
raw: &'a storage::Repository,
|
raw: &'a storage::Repository,
|
||||||
witness: PhantomData<T>,
|
witness: PhantomData<T>,
|
||||||
rng: StdRng,
|
rng: StdRng,
|
||||||
|
|
@ -107,10 +105,10 @@ impl<'a, T> Store<'a, T> {
|
||||||
/// Open a new generic store.
|
/// Open a new generic store.
|
||||||
pub fn open(whoami: PublicKey, store: &'a storage::Repository) -> Result<Self, Error> {
|
pub fn open(whoami: PublicKey, store: &'a storage::Repository) -> Result<Self, Error> {
|
||||||
let rng = rng::std();
|
let rng = rng::std();
|
||||||
let identity = Identity::load(&whoami, store)?;
|
let identity = store.identity()?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
identity,
|
parent: identity.current,
|
||||||
whoami,
|
whoami,
|
||||||
raw: store,
|
raw: store,
|
||||||
witness: PhantomData,
|
witness: PhantomData,
|
||||||
|
|
@ -151,7 +149,7 @@ where
|
||||||
cob::update(
|
cob::update(
|
||||||
self.raw,
|
self.raw,
|
||||||
signer,
|
signer,
|
||||||
&self.identity,
|
self.parent,
|
||||||
signer.public_key(),
|
signer.public_key(),
|
||||||
Update {
|
Update {
|
||||||
object_id,
|
object_id,
|
||||||
|
|
@ -175,7 +173,7 @@ where
|
||||||
let cob = cob::create(
|
let cob = cob::create(
|
||||||
self.raw,
|
self.raw,
|
||||||
signer,
|
signer,
|
||||||
&self.identity,
|
self.parent,
|
||||||
signer.public_key(),
|
signer.public_key(),
|
||||||
Create {
|
Create {
|
||||||
history_type: HISTORY_TYPE.to_owned(),
|
history_type: HISTORY_TYPE.to_owned(),
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,12 @@ use thiserror::Error;
|
||||||
use crate::crypto;
|
use crate::crypto;
|
||||||
use crate::crypto::{Signature, Verified};
|
use crate::crypto::{Signature, Verified};
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::storage::{ReadRepository, RemoteId};
|
use crate::storage;
|
||||||
|
use crate::storage::{refs, ReadRepository, RemoteId};
|
||||||
|
|
||||||
pub use crypto::PublicKey;
|
pub use crypto::PublicKey;
|
||||||
pub use did::Did;
|
pub use did::Did;
|
||||||
pub use doc::{Doc, Id, IdError};
|
pub use doc::{Doc, Id, IdError, PayloadError};
|
||||||
pub use project::Project;
|
pub use project::Project;
|
||||||
|
|
||||||
/// Untrusted, well-formed input.
|
/// Untrusted, well-formed input.
|
||||||
|
|
@ -30,18 +31,39 @@ pub enum IdentityError {
|
||||||
Git(#[from] git2::Error),
|
Git(#[from] git2::Error),
|
||||||
#[error("git: {0}")]
|
#[error("git: {0}")]
|
||||||
GitExt(#[from] git::Error),
|
GitExt(#[from] git::Error),
|
||||||
|
#[error("identity branches diverge from each other")]
|
||||||
|
BranchesDiverge,
|
||||||
#[error("root hash `{0}` does not match project")]
|
#[error("root hash `{0}` does not match project")]
|
||||||
MismatchedRoot(Oid),
|
MismatchedRoot(Oid),
|
||||||
|
#[error("the identity branch is missing")]
|
||||||
|
MissingBranch,
|
||||||
#[error("the document root is missing")]
|
#[error("the document root is missing")]
|
||||||
MissingRoot,
|
MissingRoot,
|
||||||
#[error("root commit is missing one or more delegate signatures")]
|
#[error("root commit is missing one or more delegate signatures")]
|
||||||
MissingRootSignatures,
|
MissingRootSignatures,
|
||||||
|
#[error(transparent)]
|
||||||
|
Payload(#[from] PayloadError),
|
||||||
#[error("commit signature for {0} is invalid: {1}")]
|
#[error("commit signature for {0} is invalid: {1}")]
|
||||||
InvalidSignature(PublicKey, crypto::Error),
|
InvalidSignature(PublicKey, crypto::Error),
|
||||||
#[error("threshold not reached: {0} signatures for a threshold of {1}")]
|
#[error("threshold not reached: {0} signatures for a threshold of {1}")]
|
||||||
ThresholdNotReached(usize, usize),
|
ThresholdNotReached(usize, usize),
|
||||||
#[error("identity document error: {0}")]
|
#[error("identity document error: {0}")]
|
||||||
Doc(#[from] doc::DocError),
|
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)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
|
@ -62,14 +84,6 @@ pub struct Identity<I> {
|
||||||
pub signatures: HashMap<PublicKey, Signature>,
|
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> {
|
impl Identity<Oid> {
|
||||||
pub fn verified(self, id: doc::Id) -> Result<Identity<doc::Id>, IdentityError> {
|
pub fn verified(self, id: doc::Id) -> Result<Identity<doc::Id>, IdentityError> {
|
||||||
// The root hash must be equal to the id.
|
// The root hash must be equal to the id.
|
||||||
|
|
@ -94,6 +108,11 @@ impl Identity<Untrusted> {
|
||||||
repo: &R,
|
repo: &R,
|
||||||
) -> Result<Identity<Oid>, IdentityError> {
|
) -> Result<Identity<Oid>, IdentityError> {
|
||||||
let head = Doc::<Untrusted>::head(remote, repo)?;
|
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<_>>();
|
let mut history = repo.revwalk(head)?.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Retrieve root document.
|
// Retrieve root document.
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@ use thiserror::Error;
|
||||||
|
|
||||||
use crate::crypto::{Signer, Verified};
|
use crate::crypto::{Signer, Verified};
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::identity::doc;
|
|
||||||
use crate::identity::doc::{DocError, Id};
|
use crate::identity::doc::{DocError, Id};
|
||||||
use crate::identity::project::Project;
|
use crate::identity::project::Project;
|
||||||
|
use crate::identity::{doc, IdentityError};
|
||||||
use crate::storage::git::transport;
|
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::refs::SignedRefs;
|
||||||
use crate::storage::WriteRepository;
|
use crate::storage::WriteRepository;
|
||||||
use crate::storage::{BranchName, ReadRepository as _, RemoteId};
|
use crate::storage::{BranchName, ReadRepository as _, RemoteId};
|
||||||
|
|
@ -26,7 +26,7 @@ pub enum InitError {
|
||||||
#[error("doc: {0}")]
|
#[error("doc: {0}")]
|
||||||
Doc(#[from] DocError),
|
Doc(#[from] DocError),
|
||||||
#[error("project: {0}")]
|
#[error("project: {0}")]
|
||||||
Project(#[from] storage::git::ProjectError),
|
Identity(#[from] IdentityError),
|
||||||
#[error("project payload: {0}")]
|
#[error("project payload: {0}")]
|
||||||
ProjectPayload(String),
|
ProjectPayload(String),
|
||||||
#[error("git: {0}")]
|
#[error("git: {0}")]
|
||||||
|
|
@ -102,7 +102,7 @@ pub enum ForkError {
|
||||||
#[error("project `{0}` was not found in storage")]
|
#[error("project `{0}` was not found in storage")]
|
||||||
NotFound(Id),
|
NotFound(Id),
|
||||||
#[error("project identity error: {0}")]
|
#[error("project identity error: {0}")]
|
||||||
InvalidIdentity(#[from] storage::git::ProjectError),
|
InvalidIdentity(#[from] IdentityError),
|
||||||
#[error("project identity document error: {0}")]
|
#[error("project identity document error: {0}")]
|
||||||
Doc(#[from] DocError),
|
Doc(#[from] DocError),
|
||||||
#[error("git: invalid reference")]
|
#[error("git: invalid reference")]
|
||||||
|
|
@ -199,7 +199,7 @@ pub enum CheckoutError {
|
||||||
#[error("project `{0}` was not found in storage")]
|
#[error("project `{0}` was not found in storage")]
|
||||||
NotFound(Id),
|
NotFound(Id),
|
||||||
#[error("project error: {0}")]
|
#[error("project error: {0}")]
|
||||||
Project(#[from] ProjectError),
|
Identity(#[from] IdentityError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checkout a project from storage as a working copy.
|
/// Checkout a project from storage as a working copy.
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crypto::{PublicKey, Signer, Unverified, Verified};
|
use crypto::{PublicKey, Signer, Unverified, Verified};
|
||||||
pub use git::{ProjectError, VerifyError};
|
pub use git::VerifyError;
|
||||||
pub use radicle_git_ext::Oid;
|
pub use radicle_git_ext::Oid;
|
||||||
|
|
||||||
use crate::collections::HashMap;
|
use crate::collections::HashMap;
|
||||||
|
|
@ -18,7 +18,7 @@ use crate::git::ext as git_ext;
|
||||||
use crate::git::{Qualified, RefError, RefString};
|
use crate::git::{Qualified, RefError, RefString};
|
||||||
use crate::identity;
|
use crate::identity;
|
||||||
use crate::identity::doc::DocError;
|
use crate::identity::doc::DocError;
|
||||||
use crate::identity::{Id, IdError};
|
use crate::identity::{Id, IdError, IdentityError};
|
||||||
use crate::storage::refs::Refs;
|
use crate::storage::refs::Refs;
|
||||||
|
|
||||||
use self::refs::SignedRefs;
|
use self::refs::SignedRefs;
|
||||||
|
|
@ -98,7 +98,7 @@ pub enum FetchError {
|
||||||
Storage(#[from] Error),
|
Storage(#[from] Error),
|
||||||
// TODO: This should wrap a more specific error.
|
// TODO: This should wrap a more specific error.
|
||||||
#[error("repository head: {0}")]
|
#[error("repository head: {0}")]
|
||||||
SetHead(#[from] ProjectError),
|
SetHead(#[from] IdentityError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RemoteId = PublicKey;
|
pub type RemoteId = PublicKey;
|
||||||
|
|
@ -269,9 +269,9 @@ pub trait ReadStorage {
|
||||||
&self,
|
&self,
|
||||||
remote: &RemoteId,
|
remote: &RemoteId,
|
||||||
rid: Id,
|
rid: Id,
|
||||||
) -> Result<Option<identity::Doc<Verified>>, ProjectError>;
|
) -> Result<Option<identity::Doc<Verified>>, IdentityError>;
|
||||||
/// Check whether storage contains a repository.
|
/// 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.
|
/// Get the inventory of repositories hosted under this storage.
|
||||||
fn inventory(&self) -> Result<Inventory, Error>;
|
fn inventory(&self) -> Result<Inventory, Error>;
|
||||||
/// Open or create a read-only repository.
|
/// Open or create a read-only repository.
|
||||||
|
|
@ -314,14 +314,14 @@ pub trait ReadRepository {
|
||||||
/// head using [`ReadRepository::canonical_head`].
|
/// head using [`ReadRepository::canonical_head`].
|
||||||
///
|
///
|
||||||
/// Returns the [`Oid`] as well as the qualified reference name.
|
/// 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.
|
/// Compute the canonical head of this repository.
|
||||||
///
|
///
|
||||||
/// Ignores any existing `HEAD` reference.
|
/// Ignores any existing `HEAD` reference.
|
||||||
///
|
///
|
||||||
/// Returns the [`Oid`] as well as the qualified reference name.
|
/// 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`.
|
/// Get the `reference` for the given `remote`.
|
||||||
///
|
///
|
||||||
|
|
@ -357,14 +357,14 @@ pub trait ReadRepository {
|
||||||
fn remotes(&self) -> Result<Remotes<Verified>, refs::Error>;
|
fn remotes(&self) -> Result<Remotes<Verified>, refs::Error>;
|
||||||
|
|
||||||
/// Get the repository's identity document.
|
/// 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.
|
/// Allows read-write access to a repository.
|
||||||
pub trait WriteRepository: ReadRepository {
|
pub trait WriteRepository: ReadRepository {
|
||||||
/// Set the repository head to the canonical branch.
|
/// Set the repository head to the canonical branch.
|
||||||
/// This computes the head based on the delegate set.
|
/// 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.
|
/// Sign the repository's refs under the `refs/rad/sigrefs` branch.
|
||||||
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, Error>;
|
fn sign_refs<G: Signer>(&self, signer: &G) -> Result<SignedRefs<Verified>, Error>;
|
||||||
/// Get the underlying git repository.
|
/// Get the underlying git repository.
|
||||||
|
|
@ -386,7 +386,7 @@ where
|
||||||
self.deref().path_of(rid)
|
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)
|
self.deref().contains(rid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -398,7 +398,7 @@ where
|
||||||
&self,
|
&self,
|
||||||
remote: &RemoteId,
|
remote: &RemoteId,
|
||||||
proj: Id,
|
proj: Id,
|
||||||
) -> Result<Option<identity::Doc<Verified>>, ProjectError> {
|
) -> Result<Option<identity::Doc<Verified>>, IdentityError> {
|
||||||
self.deref().get(remote, proj)
|
self.deref().get(remote, proj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::identity;
|
use crate::identity;
|
||||||
use crate::identity::{doc, Doc, Id};
|
use crate::identity::{Doc, Id};
|
||||||
use crate::identity::{Identity, IdentityError, Project};
|
use crate::identity::{Identity, IdentityError, Project};
|
||||||
use crate::storage::refs;
|
use crate::storage::refs;
|
||||||
use crate::storage::refs::{Refs, SignedRefs};
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Storage {
|
pub struct Storage {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
|
@ -105,7 +74,7 @@ impl ReadStorage for Storage {
|
||||||
paths::repository(&self, rid)
|
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() {
|
if paths::repository(&self, rid).exists() {
|
||||||
let _ = self.repository(*rid)?.head()?;
|
let _ = self.repository(*rid)?.head()?;
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
|
|
@ -113,13 +82,13 @@ impl ReadStorage for Storage {
|
||||||
Ok(false)
|
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) {
|
let repo = match self.repository(proj) {
|
||||||
Ok(doc) => doc,
|
Ok(doc) => doc,
|
||||||
Err(e) if e.is_not_found() => return Ok(None),
|
Err(e) if e.is_not_found() => return Ok(None),
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
match repo.identity_of(remote) {
|
match repo.identity_doc_of(remote) {
|
||||||
Ok(doc) => Ok(Some(doc)),
|
Ok(doc) => Ok(Some(doc)),
|
||||||
Err(e) if e.is_not_found() => Ok(None),
|
Err(e) if e.is_not_found() => Ok(None),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
|
|
@ -300,18 +269,24 @@ impl Repository {
|
||||||
Ok(refs)
|
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)
|
Identity::load(remote, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn project_of(&self, remote: &RemoteId) -> Result<Project, ProjectError> {
|
pub fn identity(&self) -> Result<Identity<Oid>, IdentityError> {
|
||||||
let doc = self.identity_of(remote)?;
|
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()?;
|
let proj = doc.project()?;
|
||||||
|
|
||||||
Ok(proj)
|
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 (doc, _) = identity::Doc::load(remote, self)?;
|
||||||
let verified = doc.verified()?;
|
let verified = doc.verified()?;
|
||||||
|
|
||||||
|
|
@ -319,8 +294,18 @@ impl Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the canonical identity [`git::Oid`] and document.
|
/// 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();
|
let mut heads = Vec::new();
|
||||||
|
|
||||||
for remote in self.remote_ids()? {
|
for remote in self.remote_ids()? {
|
||||||
let remote = remote?;
|
let remote = remote?;
|
||||||
let oid = Doc::<Unverified>::head(&remote, self)?;
|
let oid = Doc::<Unverified>::head(&remote, self)?;
|
||||||
|
|
@ -328,7 +313,7 @@ impl Repository {
|
||||||
heads.push(oid.into());
|
heads.push(oid.into());
|
||||||
}
|
}
|
||||||
// Keep track of the longest identity branch.
|
// 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 {
|
for head in &heads {
|
||||||
let base = self.raw().merge_base(*head, longest)?;
|
let base = self.raw().merge_base(*head, longest)?;
|
||||||
|
|
@ -360,13 +345,10 @@ impl Repository {
|
||||||
// o (base)
|
// o (base)
|
||||||
// |
|
// |
|
||||||
//
|
//
|
||||||
return Err(ProjectError::BranchesDiverge);
|
return Err(IdentityError::BranchesDiverge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(longest.into())
|
||||||
Doc::<Unverified>::load_at(longest.into(), self)
|
|
||||||
.map(|(doc, _)| (longest.into(), doc))
|
|
||||||
.map_err(ProjectError::from)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remote_ids(
|
pub fn remote_ids(
|
||||||
|
|
@ -484,7 +466,7 @@ impl ReadRepository for Repository {
|
||||||
return Err(VerifyError::MissingRef(remote, name));
|
return Err(VerifyError::MissingRef(remote, name));
|
||||||
}
|
}
|
||||||
// Verify identity history of remote.
|
// Verify identity history of remote.
|
||||||
self.identity(&remote)?.verified(self.id)?;
|
self.identity_of(&remote)?.verified(self.id)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -554,11 +536,11 @@ impl ReadRepository for Repository {
|
||||||
Ok(Remotes::from_iter(remotes))
|
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)
|
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 `HEAD` is already set locally, just return that.
|
||||||
if let Ok(head) = self.backend.head() {
|
if let Ok(head) = self.backend.head() {
|
||||||
if let Ok((name, oid)) = git::refs::qualified_from(&head) {
|
if let Ok((name, oid)) = git::refs::qualified_from(&head) {
|
||||||
|
|
@ -568,7 +550,7 @@ impl ReadRepository for Repository {
|
||||||
self.canonical_head()
|
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,
|
// TODO: In the `fork` function for example, we call Repository::project_identity again,
|
||||||
// This should only be necessary once.
|
// This should only be necessary once.
|
||||||
let (_, doc) = self.identity_doc()?;
|
let (_, doc) = self.identity_doc()?;
|
||||||
|
|
@ -595,7 +577,7 @@ impl ReadRepository for Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteRepository 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 head_ref = refname!("HEAD");
|
||||||
let (branch_ref, head) = self.canonical_head()?;
|
let (branch_ref, head) = self.canonical_head()?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use radicle_git_ext as git_ext;
|
||||||
|
|
||||||
use crate::crypto::{Signer, Verified};
|
use crate::crypto::{Signer, Verified};
|
||||||
use crate::identity::doc::{Doc, Id};
|
use crate::identity::doc::{Doc, Id};
|
||||||
|
use crate::identity::IdentityError;
|
||||||
|
|
||||||
pub use crate::storage::*;
|
pub use crate::storage::*;
|
||||||
|
|
||||||
|
|
@ -42,15 +43,11 @@ impl ReadStorage for MockStorage {
|
||||||
self.path().join(rid.canonical())
|
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))
|
Ok(self.inventory.contains_key(rid))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(
|
fn get(&self, _remote: &RemoteId, proj: Id) -> Result<Option<Doc<Verified>>, IdentityError> {
|
||||||
&self,
|
|
||||||
_remote: &RemoteId,
|
|
||||||
proj: Id,
|
|
||||||
) -> Result<Option<Doc<Verified>>, git::ProjectError> {
|
|
||||||
Ok(self.inventory.get(&proj).cloned())
|
Ok(self.inventory.get(&proj).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,11 +83,11 @@ impl ReadRepository for MockRepository {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn head(&self) -> Result<(fmt::Qualified, Oid), ProjectError> {
|
fn head(&self) -> Result<(fmt::Qualified, Oid), IdentityError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn canonical_head(&self) -> Result<(fmt::Qualified, Oid), ProjectError> {
|
fn canonical_head(&self) -> Result<(fmt::Qualified, Oid), IdentityError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,7 +145,7 @@ impl ReadRepository for MockRepository {
|
||||||
|
|
||||||
fn identity_doc(
|
fn identity_doc(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<(Oid, crate::identity::Doc<crate::crypto::Unverified>), git::ProjectError> {
|
) -> Result<(Oid, crate::identity::Doc<crate::crypto::Unverified>), IdentityError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +155,7 @@ impl WriteRepository for MockRepository {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_head(&self) -> Result<Oid, ProjectError> {
|
fn set_head(&self) -> Result<Oid, IdentityError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue