cob: Parametrize `cob::Store` with repository type

The additional flexibility in choosing a backend for the COB store will
allow things like "draft" COBs that are stored in a different location,
without changing much of the logic.
This commit is contained in:
Alexis Sellier 2023-06-23 12:49:57 +02:00
parent 45ccbde226
commit c9162121b0
No known key found for this signature in database
16 changed files with 111 additions and 86 deletions

View File

@ -14,7 +14,7 @@ use radicle::node::{AliasStore, Handle};
use radicle::prelude::Did; use radicle::prelude::Did;
use radicle::profile; use radicle::profile;
use radicle::storage; use radicle::storage;
use radicle::storage::WriteStorage; use radicle::storage::{WriteRepository, WriteStorage};
use radicle::{cob, Node}; use radicle::{cob, Node};
use radicle_term::table::TableOptions; use radicle_term::table::TableOptions;
use radicle_term::{Paint, Table, VStack}; use radicle_term::{Paint, Table, VStack};
@ -359,8 +359,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
fn list( fn list<R: WriteRepository + cob::Store>(
issues: &Issues, issues: &Issues<R>,
assigned: &Option<Assigned>, assigned: &Option<Assigned>,
state: &Option<State>, state: &Option<State>,
profile: &profile::Profile, profile: &profile::Profile,
@ -544,12 +544,12 @@ fn prompt_issue(
Ok(Some((meta, description))) Ok(Some((meta, description)))
} }
fn open<G: Signer>( fn open<R: WriteRepository + cob::Store, G: Signer>(
title: Option<String>, title: Option<String>,
description: Option<String>, description: Option<String>,
tags: Vec<Tag>, tags: Vec<Tag>,
options: &Options, options: &Options,
issues: &mut Issues, issues: &mut Issues<R>,
signer: &G, signer: &G,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let Some((meta, description)) = prompt_issue( let Some((meta, description)) = prompt_issue(
@ -579,8 +579,8 @@ fn open<G: Signer>(
Ok(()) Ok(())
} }
fn edit<G: radicle::crypto::Signer>( fn edit<R: WriteRepository + cob::Store, G: radicle::crypto::Signer>(
issues: &mut issue::Issues, issues: &mut issue::Issues<R>,
signer: &G, signer: &G,
repo: &storage::git::Repository, repo: &storage::git::Repository,
id: Rev, id: Rev,

View File

@ -116,9 +116,9 @@ mod tests;
/// [`git2::Repository`]. It is expected that the underlying storage /// [`git2::Repository`]. It is expected that the underlying storage
/// for `object::Storage` will also be `git2::Repository`, but if not /// for `object::Storage` will also be `git2::Repository`, but if not
/// please open an issue to change the definition of `Store` :) /// please open an issue to change the definition of `Store` :)
pub trait Store pub trait Store<I = crypto::PublicKey>
where where
Self: object::Storage Self: object::Storage<Identifier = I>
+ change::Storage< + change::Storage<
StoreError = git::change::error::Create, StoreError = git::change::error::Create,
LoadError = git::change::error::Load, LoadError = git::change::error::Load,

View File

@ -48,7 +48,7 @@ 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>( pub fn create<S, I, G>(
storage: &S, storage: &S,
signer: &G, signer: &G,
resource: Oid, resource: Oid,
@ -57,7 +57,7 @@ pub fn create<S, G>(
args: Create, args: Create,
) -> Result<CollaborativeObject, error::Create> ) -> Result<CollaborativeObject, error::Create>
where where
S: Store, S: Store<I>,
G: crypto::Signer, G: crypto::Signer,
{ {
let Create { ref typename, .. } = &args; let Create { ref typename, .. } = &args;

View File

@ -13,13 +13,13 @@ use super::error;
/// The `typename` is the type of object to be found, while the /// The `typename` is the type of object to be found, while the
/// `object_id` is the identifier for the particular object under that /// `object_id` is the identifier for the particular object under that
/// type. /// type.
pub fn get<S>( pub fn get<S, I>(
storage: &S, storage: &S,
typename: &TypeName, typename: &TypeName,
oid: &ObjectId, oid: &ObjectId,
) -> Result<Option<CollaborativeObject>, error::Retrieve> ) -> Result<Option<CollaborativeObject>, error::Retrieve>
where where
S: Store, S: Store<I>,
{ {
let tip_refs = storage let tip_refs = storage
.objects(typename, oid) .objects(typename, oid)

View File

@ -11,12 +11,12 @@ use super::error;
/// [`Store`] for further information. /// [`Store`] for further information.
/// ///
/// The `typename` is the type of objects to be listed. /// The `typename` is the type of objects to be listed.
pub fn list<S>( pub fn list<S, I>(
storage: &S, storage: &S,
typename: &TypeName, typename: &TypeName,
) -> Result<Vec<CollaborativeObject>, error::Retrieve> ) -> Result<Vec<CollaborativeObject>, error::Retrieve>
where where
S: Store, S: Store<I>,
{ {
let references = storage let references = storage
.types(typename) .types(typename)

View File

@ -13,14 +13,14 @@ use super::error;
/// The `typename` is the type of object to be found, while the /// The `typename` is the type of object to be found, while the
/// `object_id` is the identifier for the particular object under that /// `object_id` is the identifier for the particular object under that
/// type. /// type.
pub fn remove<S>( pub fn remove<S, I>(
storage: &S, storage: &S,
identifier: &S::Identifier, identifier: &S::Identifier,
typename: &TypeName, typename: &TypeName,
oid: &ObjectId, oid: &ObjectId,
) -> Result<(), error::Remove> ) -> Result<(), error::Remove>
where where
S: Store, S: Store<I>,
{ {
storage storage
.remove(identifier, typename, oid) .remove(identifier, typename, oid)

View File

@ -49,7 +49,7 @@ 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>( pub fn update<S, I, G>(
storage: &S, storage: &S,
signer: &G, signer: &G,
resource: Oid, resource: Oid,
@ -58,7 +58,7 @@ pub fn update<S, G>(
args: Update, args: Update,
) -> Result<Updated, error::Update> ) -> Result<Updated, error::Update>
where where
S: Store, S: Store<I>,
G: crypto::Signer, G: crypto::Signer,
{ {
let Update { let Update {

View File

@ -10,7 +10,7 @@ use crate::{ObjectId, TypeName};
/// The [`Reference`]s that refer to the commits that make up a /// The [`Reference`]s that refer to the commits that make up a
/// [`crate::CollaborativeObject`]. /// [`crate::CollaborativeObject`].
#[derive(Clone, Debug)] #[derive(Clone, Debug, Default)]
pub struct Objects(Vec<Reference>); pub struct Objects(Vec<Reference>);
impl Objects { impl Objects {

View File

@ -56,7 +56,7 @@ impl Storage {
} }
} }
impl Store for Storage {} impl Store<Urn> for Storage {}
impl change::Storage for Storage { impl change::Storage for Storage {
type StoreError = <git2::Repository as change::Storage>::StoreError; type StoreError = <git2::Repository as change::Storage>::StoreError;

View File

@ -9,11 +9,11 @@ pub mod thread;
#[cfg(test)] #[cfg(test)]
pub mod test; pub mod test;
pub use cob::{create, get, list, remove, update};
pub use cob::{ pub use cob::{
history::EntryId, object::collaboration::error, CollaborativeObject, Contents, Create, Entry, change, history::EntryId, object, object::collaboration::error, CollaborativeObject, Contents,
History, ObjectId, TypeName, Update, Updated, Create, Entry, History, ObjectId, Store, TypeName, Update, Updated,
}; };
pub use cob::{create, get, list, remove, update};
pub use common::*; pub use common::*;
pub use op::{ActorId, Op}; pub use op::{ActorId, Op};

View File

@ -17,7 +17,7 @@ use crate::{
}, },
identity::{doc::DocError, Did, Identity, IdentityError}, identity::{doc::DocError, Did, Identity, IdentityError},
prelude::{Doc, ReadRepository}, prelude::{Doc, ReadRepository},
storage::{git as storage, RemoteId, WriteRepository}, storage::{RemoteId, WriteRepository},
}; };
use super::{ use super::{
@ -561,20 +561,23 @@ impl store::Transaction<Proposal> {
} }
} }
pub struct ProposalMut<'a, 'g> { pub struct ProposalMut<'a, 'g, R> {
pub id: ObjectId, pub id: ObjectId,
proposal: Proposal, proposal: Proposal,
clock: clock::Lamport, clock: clock::Lamport,
store: &'g mut Proposals<'a>, store: &'g mut Proposals<'a, R>,
} }
impl<'a, 'g> ProposalMut<'a, 'g> { impl<'a, 'g, R> ProposalMut<'a, 'g, R>
where
R: WriteRepository + cob::Store,
{
pub fn new( pub fn new(
id: ObjectId, id: ObjectId,
proposal: Proposal, proposal: Proposal,
clock: clock::Lamport, clock: clock::Lamport,
store: &'g mut Proposals<'a>, store: &'g mut Proposals<'a, R>,
) -> Self { ) -> Self {
Self { Self {
id, id,
@ -672,7 +675,7 @@ impl<'a, 'g> ProposalMut<'a, 'g> {
} }
} }
impl<'a, 'g> Deref for ProposalMut<'a, 'g> { impl<'a, 'g, R> Deref for ProposalMut<'a, 'g, R> {
type Target = Proposal; type Target = Proposal;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -680,21 +683,24 @@ impl<'a, 'g> Deref for ProposalMut<'a, 'g> {
} }
} }
pub struct Proposals<'a> { pub struct Proposals<'a, R> {
raw: store::Store<'a, Proposal>, raw: store::Store<'a, Proposal, R>,
} }
impl<'a> Deref for Proposals<'a> { impl<'a, R> Deref for Proposals<'a, R> {
type Target = store::Store<'a, Proposal>; type Target = store::Store<'a, Proposal, R>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.raw &self.raw
} }
} }
impl<'a> Proposals<'a> { impl<'a, R: WriteRepository> Proposals<'a, R>
where
R: WriteRepository + cob::Store,
{
/// Open a proposals store. /// Open a proposals store.
pub fn open(repository: &'a storage::Repository) -> Result<Self, store::Error> { pub fn open(repository: &'a R) -> Result<Self, store::Error> {
let raw = store::Store::open(repository)?; let raw = store::Store::open(repository)?;
Ok(Self { raw }) Ok(Self { raw })
@ -708,7 +714,7 @@ impl<'a> Proposals<'a> {
current: impl Into<Oid>, current: impl Into<Oid>,
proposed: Doc<Verified>, proposed: Doc<Verified>,
signer: &G, signer: &G,
) -> Result<ProposalMut<'a, 'g>, Error> { ) -> Result<ProposalMut<'a, 'g, R>, Error> {
let (id, proposal, clock) = let (id, proposal, clock) =
Transaction::initial("Create proposal", &mut self.raw, signer, |tx| { Transaction::initial("Create proposal", &mut self.raw, signer, |tx| {
tx.revision(current.into(), proposed)?; tx.revision(current.into(), proposed)?;
@ -728,7 +734,10 @@ impl<'a> Proposals<'a> {
} }
/// Get a proposal mutably. /// Get a proposal mutably.
pub fn get_mut<'g>(&'g mut self, id: &ObjectId) -> Result<ProposalMut<'a, 'g>, store::Error> { pub fn get_mut<'g>(
&'g mut self,
id: &ObjectId,
) -> Result<ProposalMut<'a, 'g, R>, store::Error> {
let (proposal, clock) = self let (proposal, clock) = self
.raw .raw
.get(id)? .get(id)?

View File

@ -17,7 +17,7 @@ use crate::cob::thread::{CommentId, Thread};
use crate::cob::{store, ActorId, EntryId, ObjectId, TypeName}; use crate::cob::{store, ActorId, EntryId, ObjectId, TypeName};
use crate::crypto::Signer; use crate::crypto::Signer;
use crate::prelude::{Did, ReadRepository}; use crate::prelude::{Did, ReadRepository};
use crate::storage::git as storage; use crate::storage::WriteRepository;
/// Issue operation. /// Issue operation.
pub type Op = cob::Op<Action>; pub type Op = cob::Op<Action>;
@ -331,14 +331,17 @@ impl store::Transaction<Issue> {
} }
} }
pub struct IssueMut<'a, 'g> { pub struct IssueMut<'a, 'g, R> {
id: ObjectId, id: ObjectId,
clock: clock::Lamport, clock: clock::Lamport,
issue: Issue, issue: Issue,
store: &'g mut Issues<'a>, store: &'g mut Issues<'a, R>,
} }
impl<'a, 'g> IssueMut<'a, 'g> { impl<'a, 'g, R> IssueMut<'a, 'g, R>
where
R: WriteRepository + cob::Store,
{
/// Get the issue id. /// Get the issue id.
pub fn id(&self) -> &ObjectId { pub fn id(&self) -> &ObjectId {
&self.id &self.id
@ -457,7 +460,7 @@ impl<'a, 'g> IssueMut<'a, 'g> {
} }
} }
impl<'a, 'g> Deref for IssueMut<'a, 'g> { impl<'a, 'g, R> Deref for IssueMut<'a, 'g, R> {
type Target = Issue; type Target = Issue;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -465,12 +468,12 @@ impl<'a, 'g> Deref for IssueMut<'a, 'g> {
} }
} }
pub struct Issues<'a> { pub struct Issues<'a, R> {
raw: store::Store<'a, Issue>, raw: store::Store<'a, Issue, R>,
} }
impl<'a> Deref for Issues<'a> { impl<'a, R> Deref for Issues<'a, R> {
type Target = store::Store<'a, Issue>; type Target = store::Store<'a, Issue, R>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.raw &self.raw
@ -485,9 +488,12 @@ pub struct IssueCounts {
pub closed: usize, pub closed: usize,
} }
impl<'a> Issues<'a> { impl<'a, R: WriteRepository> Issues<'a, R>
where
R: ReadRepository + cob::Store,
{
/// Open an issues store. /// Open an issues store.
pub fn open(repository: &'a storage::Repository) -> Result<Self, store::Error> { pub fn open(repository: &'a R) -> Result<Self, store::Error> {
let raw = store::Store::open(repository)?; let raw = store::Store::open(repository)?;
Ok(Self { raw }) Ok(Self { raw })
@ -499,7 +505,7 @@ impl<'a> Issues<'a> {
} }
/// Get an issue mutably. /// Get an issue mutably.
pub fn get_mut<'g>(&'g mut self, id: &ObjectId) -> Result<IssueMut<'a, 'g>, store::Error> { pub fn get_mut<'g>(&'g mut self, id: &ObjectId) -> Result<IssueMut<'a, 'g, R>, store::Error> {
let (issue, clock) = self let (issue, clock) = self
.raw .raw
.get(id)? .get(id)?
@ -521,7 +527,7 @@ impl<'a> Issues<'a> {
tags: &[Tag], tags: &[Tag],
assignees: &[ActorId], assignees: &[ActorId],
signer: &G, signer: &G,
) -> Result<IssueMut<'a, 'g>, Error> { ) -> Result<IssueMut<'a, 'g, R>, Error> {
let (id, issue, clock) = let (id, issue, clock) =
Transaction::initial("Create issue", &mut self.raw, signer, |tx| { Transaction::initial("Create issue", &mut self.raw, signer, |tx| {
tx.thread(description)?; tx.thread(description)?;

View File

@ -30,7 +30,6 @@ use crate::identity;
use crate::identity::doc::DocError; use crate::identity::doc::DocError;
use crate::identity::PayloadError; use crate::identity::PayloadError;
use crate::prelude::*; use crate::prelude::*;
use crate::storage::git as storage;
/// The logical clock we use to order operations to patches. /// The logical clock we use to order operations to patches.
pub use clock::Lamport as Clock; pub use clock::Lamport as Clock;
@ -1143,20 +1142,23 @@ impl store::Transaction<Patch> {
} }
} }
pub struct PatchMut<'a, 'g> { pub struct PatchMut<'a, 'g, R> {
pub id: ObjectId, pub id: ObjectId,
patch: Patch, patch: Patch,
clock: clock::Lamport, clock: clock::Lamport,
store: &'g mut Patches<'a>, store: &'g mut Patches<'a, R>,
} }
impl<'a, 'g> PatchMut<'a, 'g> { impl<'a, 'g, R> PatchMut<'a, 'g, R>
where
R: WriteRepository + cob::Store,
{
pub fn new( pub fn new(
id: ObjectId, id: ObjectId,
patch: Patch, patch: Patch,
clock: clock::Lamport, clock: clock::Lamport,
store: &'g mut Patches<'a>, store: &'g mut Patches<'a, R>,
) -> Self { ) -> Self {
Self { Self {
id, id,
@ -1358,7 +1360,7 @@ impl<'a, 'g> PatchMut<'a, 'g> {
} }
} }
impl<'a, 'g> Deref for PatchMut<'a, 'g> { impl<'a, 'g, R> Deref for PatchMut<'a, 'g, R> {
type Target = Patch; type Target = Patch;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -1376,21 +1378,24 @@ pub struct PatchCounts {
pub merged: usize, pub merged: usize,
} }
pub struct Patches<'a> { pub struct Patches<'a, R> {
raw: store::Store<'a, Patch>, raw: store::Store<'a, Patch, R>,
} }
impl<'a> Deref for Patches<'a> { impl<'a, R> Deref for Patches<'a, R> {
type Target = store::Store<'a, Patch>; type Target = store::Store<'a, Patch, R>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.raw &self.raw
} }
} }
impl<'a> Patches<'a> { impl<'a, R> Patches<'a, R>
where
R: WriteRepository + cob::Store,
{
/// Open an patches store. /// Open an patches store.
pub fn open(repository: &'a storage::Repository) -> Result<Self, store::Error> { pub fn open(repository: &'a R) -> Result<Self, store::Error> {
let raw = store::Store::open(repository)?; let raw = store::Store::open(repository)?;
Ok(Self { raw }) Ok(Self { raw })
@ -1406,7 +1411,7 @@ impl<'a> Patches<'a> {
oid: impl Into<git::Oid>, oid: impl Into<git::Oid>,
tags: &[Tag], tags: &[Tag],
signer: &G, signer: &G,
) -> Result<PatchMut<'a, 'g>, Error> { ) -> Result<PatchMut<'a, 'g, R>, Error> {
self._create( self._create(
title, title,
description, description,
@ -1429,7 +1434,7 @@ impl<'a> Patches<'a> {
oid: impl Into<git::Oid>, oid: impl Into<git::Oid>,
tags: &[Tag], tags: &[Tag],
signer: &G, signer: &G,
) -> Result<PatchMut<'a, 'g>, Error> { ) -> Result<PatchMut<'a, 'g, R>, Error> {
self._create( self._create(
title, title,
description, description,
@ -1484,7 +1489,7 @@ impl<'a> Patches<'a> {
} }
/// Get a patch mutably. /// Get a patch mutably.
pub fn get_mut<'g>(&'g mut self, id: &ObjectId) -> Result<PatchMut<'a, 'g>, store::Error> { pub fn get_mut<'g>(&'g mut self, id: &ObjectId) -> Result<PatchMut<'a, 'g, R>, store::Error> {
let (patch, clock) = self let (patch, clock) = self
.raw .raw
.get(id)? .get(id)?
@ -1531,7 +1536,7 @@ impl<'a> Patches<'a> {
tags: &[Tag], tags: &[Tag],
state: State, state: State,
signer: &G, signer: &G,
) -> Result<PatchMut<'a, 'g>, Error> { ) -> Result<PatchMut<'a, 'g, R>, Error> {
let (id, patch, clock) = let (id, patch, clock) =
Transaction::initial("Create patch", &mut self.raw, signer, |tx| { Transaction::initial("Create patch", &mut self.raw, signer, |tx| {
tx.revision(description, base, oid)?; tx.revision(description, base, oid)?;

View File

@ -128,21 +128,21 @@ impl 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, R> {
identity: git::Oid, identity: git::Oid,
repo: &'a storage::Repository, repo: &'a R,
witness: PhantomData<T>, witness: PhantomData<T>,
} }
impl<'a, T> AsRef<storage::Repository> for Store<'a, T> { impl<'a, T, R> AsRef<R> for Store<'a, T, R> {
fn as_ref(&self) -> &storage::Repository { fn as_ref(&self) -> &R {
self.repo self.repo
} }
} }
impl<'a, T> Store<'a, T> { impl<'a, T, R: ReadRepository> Store<'a, T, R> {
/// Open a new generic store. /// Open a new generic store.
pub fn open(repo: &'a storage::Repository) -> Result<Self, Error> { pub fn open(repo: &'a R) -> Result<Self, Error> {
let identity = repo.identity()?; let identity = repo.identity()?;
Ok(Self { Ok(Self {
@ -153,8 +153,10 @@ impl<'a, T> Store<'a, T> {
} }
} }
impl<'a, T: FromHistory> Store<'a, T> impl<'a, T, R> Store<'a, T, R>
where where
R: WriteRepository + cob::Store,
T: FromHistory,
T::Action: Serialize, T::Action: Serialize,
{ {
/// Update an object. /// Update an object.
@ -299,15 +301,16 @@ impl<T: FromHistory> Transaction<T> {
} }
/// Create a new transaction to be used as the initial set of operations for a COB. /// Create a new transaction to be used as the initial set of operations for a COB.
pub fn initial<G, F>( pub fn initial<R, G, F>(
message: &str, message: &str,
store: &mut Store<T>, store: &mut Store<T, R>,
signer: &G, signer: &G,
operations: F, operations: F,
) -> Result<(ObjectId, T, Lamport), Error> ) -> Result<(ObjectId, T, Lamport), Error>
where where
G: Signer, G: Signer,
F: FnOnce(&mut Self) -> Result<(), Error>, F: FnOnce(&mut Self) -> Result<(), Error>,
R: WriteRepository + cob::Store,
T::Action: Serialize + Clone, T::Action: Serialize + Clone,
{ {
let actor = *signer.public_key(); let actor = *signer.public_key();
@ -339,14 +342,15 @@ impl<T: FromHistory> Transaction<T> {
/// Commit transaction. /// Commit transaction.
/// ///
/// Returns a list of operations that can be applied onto an in-memory CRDT. /// Returns a list of operations that can be applied onto an in-memory CRDT.
pub fn commit<G: Signer>( pub fn commit<R, G: Signer>(
mut self, mut self,
msg: &str, msg: &str,
id: ObjectId, id: ObjectId,
store: &mut Store<T>, store: &mut Store<T, R>,
signer: &G, signer: &G,
) -> Result<(Vec<cob::Op<T::Action>>, Lamport, EntryId), Error> ) -> Result<(Vec<cob::Op<T::Action>>, Lamport, EntryId), Error>
where where
R: WriteRepository + cob::Store,
T::Action: Serialize + Clone, T::Action: Serialize + Clone,
{ {
let actions = NonEmpty::from_vec(self.actions) let actions = NonEmpty::from_vec(self.actions)

View File

@ -20,7 +20,7 @@ use crate::git::{refspec::Refspec, PatternString, Qualified, RefError, RefString
use crate::identity; use crate::identity;
use crate::identity::doc::DocError; use crate::identity::doc::DocError;
use crate::identity::Did; use crate::identity::Did;
use crate::identity::{Id, IdentityError}; use crate::identity::{Id, Identity, IdentityError};
use crate::storage::git::NAMESPACES_GLOB; use crate::storage::git::NAMESPACES_GLOB;
use crate::storage::refs::Refs; use crate::storage::refs::Refs;
@ -319,7 +319,7 @@ pub trait WriteStorage: ReadStorage {
} }
/// Allows read-only access to a repository. /// Allows read-only access to a repository.
pub trait ReadRepository { pub trait ReadRepository: Sized {
/// Return the repository id. /// Return the repository id.
fn id(&self) -> Id; fn id(&self) -> Id;
@ -368,6 +368,13 @@ pub trait ReadRepository {
/// `rad/id` using [`ReadRepository::canonical_identity_head`]. /// `rad/id` using [`ReadRepository::canonical_identity_head`].
fn identity_head(&self) -> Result<Oid, IdentityError>; fn identity_head(&self) -> Result<Oid, IdentityError>;
/// Load the canonical identity.
fn identity(&self) -> Result<Identity<Oid>, IdentityError> {
let head = self.identity_head()?;
Identity::load_at(head, self)
}
/// Compute the canonical `rad/id` of this repository. /// Compute the canonical `rad/id` of this repository.
/// ///
/// Ignores any existing `rad/id` reference. /// Ignores any existing `rad/id` reference.

View File

@ -291,12 +291,6 @@ impl Repository {
Identity::load(remote, self) Identity::load(remote, self)
} }
pub fn identity(&self) -> Result<Identity<Oid>, IdentityError> {
let head = self.identity_head()?;
Identity::load_at(head, self)
}
/// Get the canonical project information. /// Get the canonical project information.
pub fn project(&self) -> Result<Project, IdentityError> { pub fn project(&self) -> Result<Project, IdentityError> {
let head = self.identity_head()?; let head = self.identity_head()?;