Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cob: Parametrize `cob::Store` with repository type
Alexis Sellier committed 2 years ago
commit c9162121b078f036405e9c4fd9358789e20259a2
parent 45ccbde226b2a9e47c0c7b11cd29b201dd356635
16 files changed +111 -86
modified radicle-cli/src/commands/issue.rs
@@ -14,7 +14,7 @@ use radicle::node::{AliasStore, Handle};
use radicle::prelude::Did;
use radicle::profile;
use radicle::storage;
-
use radicle::storage::WriteStorage;
+
use radicle::storage::{WriteRepository, WriteStorage};
use radicle::{cob, Node};
use radicle_term::table::TableOptions;
use radicle_term::{Paint, Table, VStack};
@@ -359,8 +359,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
    Ok(())
}

-
fn list(
-
    issues: &Issues,
+
fn list<R: WriteRepository + cob::Store>(
+
    issues: &Issues<R>,
    assigned: &Option<Assigned>,
    state: &Option<State>,
    profile: &profile::Profile,
@@ -544,12 +544,12 @@ fn prompt_issue(
    Ok(Some((meta, description)))
}

-
fn open<G: Signer>(
+
fn open<R: WriteRepository + cob::Store, G: Signer>(
    title: Option<String>,
    description: Option<String>,
    tags: Vec<Tag>,
    options: &Options,
-
    issues: &mut Issues,
+
    issues: &mut Issues<R>,
    signer: &G,
) -> anyhow::Result<()> {
    let Some((meta, description)) = prompt_issue(
@@ -579,8 +579,8 @@ fn open<G: Signer>(
    Ok(())
}

-
fn edit<G: radicle::crypto::Signer>(
-
    issues: &mut issue::Issues,
+
fn edit<R: WriteRepository + cob::Store, G: radicle::crypto::Signer>(
+
    issues: &mut issue::Issues<R>,
    signer: &G,
    repo: &storage::git::Repository,
    id: Rev,
modified radicle-cob/src/lib.rs
@@ -116,9 +116,9 @@ mod tests;
/// [`git2::Repository`]. It is expected that the underlying storage
/// for `object::Storage` will also be `git2::Repository`, but if not
/// please open an issue to change the definition of `Store` :)
-
pub trait Store
+
pub trait Store<I = crypto::PublicKey>
where
-
    Self: object::Storage
+
    Self: object::Storage<Identifier = I>
        + change::Storage<
            StoreError = git::change::error::Create,
            LoadError = git::change::error::Load,
modified radicle-cob/src/object/collaboration/create.rs
@@ -48,7 +48,7 @@ impl Create {
///
/// The `args` are the metadata for this [`CollaborativeObject`]. See
/// [`Create`] for further information.
-
pub fn create<S, G>(
+
pub fn create<S, I, G>(
    storage: &S,
    signer: &G,
    resource: Oid,
@@ -57,7 +57,7 @@ pub fn create<S, G>(
    args: Create,
) -> Result<CollaborativeObject, error::Create>
where
-
    S: Store,
+
    S: Store<I>,
    G: crypto::Signer,
{
    let Create { ref typename, .. } = &args;
modified radicle-cob/src/object/collaboration/get.rs
@@ -13,13 +13,13 @@ use super::error;
/// The `typename` is the type of object to be found, while the
/// `object_id` is the identifier for the particular object under that
/// type.
-
pub fn get<S>(
+
pub fn get<S, I>(
    storage: &S,
    typename: &TypeName,
    oid: &ObjectId,
) -> Result<Option<CollaborativeObject>, error::Retrieve>
where
-
    S: Store,
+
    S: Store<I>,
{
    let tip_refs = storage
        .objects(typename, oid)
modified radicle-cob/src/object/collaboration/list.rs
@@ -11,12 +11,12 @@ use super::error;
/// [`Store`] for further information.
///
/// The `typename` is the type of objects to be listed.
-
pub fn list<S>(
+
pub fn list<S, I>(
    storage: &S,
    typename: &TypeName,
) -> Result<Vec<CollaborativeObject>, error::Retrieve>
where
-
    S: Store,
+
    S: Store<I>,
{
    let references = storage
        .types(typename)
modified radicle-cob/src/object/collaboration/remove.rs
@@ -13,14 +13,14 @@ use super::error;
/// The `typename` is the type of object to be found, while the
/// `object_id` is the identifier for the particular object under that
/// type.
-
pub fn remove<S>(
+
pub fn remove<S, I>(
    storage: &S,
    identifier: &S::Identifier,
    typename: &TypeName,
    oid: &ObjectId,
) -> Result<(), error::Remove>
where
-
    S: Store,
+
    S: Store<I>,
{
    storage
        .remove(identifier, typename, oid)
modified radicle-cob/src/object/collaboration/update.rs
@@ -49,7 +49,7 @@ pub struct Update {
///
/// The `args` are the metadata for this [`CollaborativeObject`]
/// udpate. See [`Update`] for further information.
-
pub fn update<S, G>(
+
pub fn update<S, I, G>(
    storage: &S,
    signer: &G,
    resource: Oid,
@@ -58,7 +58,7 @@ pub fn update<S, G>(
    args: Update,
) -> Result<Updated, error::Update>
where
-
    S: Store,
+
    S: Store<I>,
    G: crypto::Signer,
{
    let Update {
modified radicle-cob/src/object/storage.rs
@@ -10,7 +10,7 @@ use crate::{ObjectId, TypeName};

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

impl Objects {
modified radicle-cob/src/test/storage.rs
@@ -56,7 +56,7 @@ impl Storage {
    }
}

-
impl Store for Storage {}
+
impl Store<Urn> for Storage {}

impl change::Storage for Storage {
    type StoreError = <git2::Repository as change::Storage>::StoreError;
modified radicle/src/cob.rs
@@ -9,11 +9,11 @@ pub mod thread;
#[cfg(test)]
pub mod test;

-
pub use cob::{create, get, list, remove, update};
pub use cob::{
-
    history::EntryId, object::collaboration::error, CollaborativeObject, Contents, Create, Entry,
-
    History, ObjectId, TypeName, Update, Updated,
+
    change, history::EntryId, object, object::collaboration::error, CollaborativeObject, Contents,
+
    Create, Entry, History, ObjectId, Store, TypeName, Update, Updated,
};
+
pub use cob::{create, get, list, remove, update};
pub use common::*;
pub use op::{ActorId, Op};

modified radicle/src/cob/identity.rs
@@ -17,7 +17,7 @@ use crate::{
    },
    identity::{doc::DocError, Did, Identity, IdentityError},
    prelude::{Doc, ReadRepository},
-
    storage::{git as storage, RemoteId, WriteRepository},
+
    storage::{RemoteId, WriteRepository},
};

use super::{
@@ -561,20 +561,23 @@ impl store::Transaction<Proposal> {
    }
}

-
pub struct ProposalMut<'a, 'g> {
+
pub struct ProposalMut<'a, 'g, R> {
    pub id: ObjectId,

    proposal: Proposal,
    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(
        id: ObjectId,
        proposal: Proposal,
        clock: clock::Lamport,
-
        store: &'g mut Proposals<'a>,
+
        store: &'g mut Proposals<'a, R>,
    ) -> Self {
        Self {
            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;

    fn deref(&self) -> &Self::Target {
@@ -680,21 +683,24 @@ impl<'a, 'g> Deref for ProposalMut<'a, 'g> {
    }
}

-
pub struct Proposals<'a> {
-
    raw: store::Store<'a, Proposal>,
+
pub struct Proposals<'a, R> {
+
    raw: store::Store<'a, Proposal, R>,
}

-
impl<'a> Deref for Proposals<'a> {
-
    type Target = store::Store<'a, Proposal>;
+
impl<'a, R> Deref for Proposals<'a, R> {
+
    type Target = store::Store<'a, Proposal, R>;

    fn deref(&self) -> &Self::Target {
        &self.raw
    }
}

-
impl<'a> Proposals<'a> {
+
impl<'a, R: WriteRepository> Proposals<'a, R>
+
where
+
    R: WriteRepository + cob::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)?;

        Ok(Self { raw })
@@ -708,7 +714,7 @@ impl<'a> Proposals<'a> {
        current: impl Into<Oid>,
        proposed: Doc<Verified>,
        signer: &G,
-
    ) -> Result<ProposalMut<'a, 'g>, Error> {
+
    ) -> Result<ProposalMut<'a, 'g, R>, Error> {
        let (id, proposal, clock) =
            Transaction::initial("Create proposal", &mut self.raw, signer, |tx| {
                tx.revision(current.into(), proposed)?;
@@ -728,7 +734,10 @@ impl<'a> Proposals<'a> {
    }

    /// 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
            .raw
            .get(id)?
modified radicle/src/cob/issue.rs
@@ -17,7 +17,7 @@ use crate::cob::thread::{CommentId, Thread};
use crate::cob::{store, ActorId, EntryId, ObjectId, TypeName};
use crate::crypto::Signer;
use crate::prelude::{Did, ReadRepository};
-
use crate::storage::git as storage;
+
use crate::storage::WriteRepository;

/// Issue operation.
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,
    clock: clock::Lamport,
    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.
    pub fn id(&self) -> &ObjectId {
        &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;

    fn deref(&self) -> &Self::Target {
@@ -465,12 +468,12 @@ impl<'a, 'g> Deref for IssueMut<'a, 'g> {
    }
}

-
pub struct Issues<'a> {
-
    raw: store::Store<'a, Issue>,
+
pub struct Issues<'a, R> {
+
    raw: store::Store<'a, Issue, R>,
}

-
impl<'a> Deref for Issues<'a> {
-
    type Target = store::Store<'a, Issue>;
+
impl<'a, R> Deref for Issues<'a, R> {
+
    type Target = store::Store<'a, Issue, R>;

    fn deref(&self) -> &Self::Target {
        &self.raw
@@ -485,9 +488,12 @@ pub struct IssueCounts {
    pub closed: usize,
}

-
impl<'a> Issues<'a> {
+
impl<'a, R: WriteRepository> Issues<'a, R>
+
where
+
    R: ReadRepository + cob::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)?;

        Ok(Self { raw })
@@ -499,7 +505,7 @@ impl<'a> Issues<'a> {
    }

    /// 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
            .raw
            .get(id)?
@@ -521,7 +527,7 @@ impl<'a> Issues<'a> {
        tags: &[Tag],
        assignees: &[ActorId],
        signer: &G,
-
    ) -> Result<IssueMut<'a, 'g>, Error> {
+
    ) -> Result<IssueMut<'a, 'g, R>, Error> {
        let (id, issue, clock) =
            Transaction::initial("Create issue", &mut self.raw, signer, |tx| {
                tx.thread(description)?;
modified radicle/src/cob/patch.rs
@@ -30,7 +30,6 @@ use crate::identity;
use crate::identity::doc::DocError;
use crate::identity::PayloadError;
use crate::prelude::*;
-
use crate::storage::git as storage;

/// The logical clock we use to order operations to patches.
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,

    patch: Patch,
    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(
        id: ObjectId,
        patch: Patch,
        clock: clock::Lamport,
-
        store: &'g mut Patches<'a>,
+
        store: &'g mut Patches<'a, R>,
    ) -> Self {
        Self {
            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;

    fn deref(&self) -> &Self::Target {
@@ -1376,21 +1378,24 @@ pub struct PatchCounts {
    pub merged: usize,
}

-
pub struct Patches<'a> {
-
    raw: store::Store<'a, Patch>,
+
pub struct Patches<'a, R> {
+
    raw: store::Store<'a, Patch, R>,
}

-
impl<'a> Deref for Patches<'a> {
-
    type Target = store::Store<'a, Patch>;
+
impl<'a, R> Deref for Patches<'a, R> {
+
    type Target = store::Store<'a, Patch, R>;

    fn deref(&self) -> &Self::Target {
        &self.raw
    }
}

-
impl<'a> Patches<'a> {
+
impl<'a, R> Patches<'a, R>
+
where
+
    R: WriteRepository + cob::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)?;

        Ok(Self { raw })
@@ -1406,7 +1411,7 @@ impl<'a> Patches<'a> {
        oid: impl Into<git::Oid>,
        tags: &[Tag],
        signer: &G,
-
    ) -> Result<PatchMut<'a, 'g>, Error> {
+
    ) -> Result<PatchMut<'a, 'g, R>, Error> {
        self._create(
            title,
            description,
@@ -1429,7 +1434,7 @@ impl<'a> Patches<'a> {
        oid: impl Into<git::Oid>,
        tags: &[Tag],
        signer: &G,
-
    ) -> Result<PatchMut<'a, 'g>, Error> {
+
    ) -> Result<PatchMut<'a, 'g, R>, Error> {
        self._create(
            title,
            description,
@@ -1484,7 +1489,7 @@ impl<'a> Patches<'a> {
    }

    /// 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
            .raw
            .get(id)?
@@ -1531,7 +1536,7 @@ impl<'a> Patches<'a> {
        tags: &[Tag],
        state: State,
        signer: &G,
-
    ) -> Result<PatchMut<'a, 'g>, Error> {
+
    ) -> Result<PatchMut<'a, 'g, R>, Error> {
        let (id, patch, clock) =
            Transaction::initial("Create patch", &mut self.raw, signer, |tx| {
                tx.revision(description, base, oid)?;
modified radicle/src/cob/store.rs
@@ -128,21 +128,21 @@ impl Error {
}

/// 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,
-
    repo: &'a storage::Repository,
+
    repo: &'a R,
    witness: PhantomData<T>,
}

-
impl<'a, T> AsRef<storage::Repository> for Store<'a, T> {
-
    fn as_ref(&self) -> &storage::Repository {
+
impl<'a, T, R> AsRef<R> for Store<'a, T, R> {
+
    fn as_ref(&self) -> &R {
        self.repo
    }
}

-
impl<'a, T> Store<'a, T> {
+
impl<'a, T, R: ReadRepository> Store<'a, T, R> {
    /// 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()?;

        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
+
    R: WriteRepository + cob::Store,
+
    T: FromHistory,
    T::Action: Serialize,
{
    /// 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.
-
    pub fn initial<G, F>(
+
    pub fn initial<R, G, F>(
        message: &str,
-
        store: &mut Store<T>,
+
        store: &mut Store<T, R>,
        signer: &G,
        operations: F,
    ) -> Result<(ObjectId, T, Lamport), Error>
    where
        G: Signer,
        F: FnOnce(&mut Self) -> Result<(), Error>,
+
        R: WriteRepository + cob::Store,
        T::Action: Serialize + Clone,
    {
        let actor = *signer.public_key();
@@ -339,14 +342,15 @@ impl<T: FromHistory> Transaction<T> {
    /// Commit transaction.
    ///
    /// 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,
        msg: &str,
        id: ObjectId,
-
        store: &mut Store<T>,
+
        store: &mut Store<T, R>,
        signer: &G,
    ) -> Result<(Vec<cob::Op<T::Action>>, Lamport, EntryId), Error>
    where
+
        R: WriteRepository + cob::Store,
        T::Action: Serialize + Clone,
    {
        let actions = NonEmpty::from_vec(self.actions)
modified radicle/src/storage.rs
@@ -20,7 +20,7 @@ use crate::git::{refspec::Refspec, PatternString, Qualified, RefError, RefString
use crate::identity;
use crate::identity::doc::DocError;
use crate::identity::Did;
-
use crate::identity::{Id, IdentityError};
+
use crate::identity::{Id, Identity, IdentityError};
use crate::storage::git::NAMESPACES_GLOB;
use crate::storage::refs::Refs;

@@ -319,7 +319,7 @@ pub trait WriteStorage: ReadStorage {
}

/// Allows read-only access to a repository.
-
pub trait ReadRepository {
+
pub trait ReadRepository: Sized {
    /// Return the repository id.
    fn id(&self) -> Id;

@@ -368,6 +368,13 @@ pub trait ReadRepository {
    /// `rad/id` using [`ReadRepository::canonical_identity_head`].
    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.
    ///
    /// Ignores any existing `rad/id` reference.
modified radicle/src/storage/git.rs
@@ -291,12 +291,6 @@ impl Repository {
        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.
    pub fn project(&self) -> Result<Project, IdentityError> {
        let head = self.identity_head()?;