Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
radicle: Introduce `cob::DraftStore`
Alexis Sellier committed 2 years ago
commit ff7e9ded1707aeba59d4c5db2795a0805c003413
parent c9162121b078f036405e9c4fd9358789e20259a2
4 files changed +169 -6
modified radicle-cli/src/commands/review.rs
@@ -276,7 +276,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
            }
        }
        Operation::Delete => {
-
            let name = git::refs::storage::review(profile.id(), &patch_id);
+
            let name = git::refs::storage::draft::review(profile.id(), &patch_id);

            match repository.backend.find_reference(&name) {
                Ok(mut r) => r.delete()?,
modified radicle-cli/src/commands/review/builder.rs
@@ -182,7 +182,7 @@ impl<'a> ReviewBuilder<'a> {
    pub fn new(patch_id: PatchId, nid: NodeId, repo: &'a Repository) -> Self {
        Self {
            patch_id,
-
            refname: git::refs::storage::review(&nid, &patch_id),
+
            refname: git::refs::storage::draft::review(&nid, &patch_id),
            repo,
            hunk: None,
            verdict: None,
modified radicle/src/git.rs
@@ -241,12 +241,57 @@ pub mod refs {
            .with_namespace(remote.into())
        }

-
        /// Review draft reference.
+
        /// Draft references.
        ///
-
        /// When building a patch review, we store the intermediate state in this ref.
-
        pub fn review<'a>(remote: &RemoteId, patch: &cob::ObjectId) -> Namespaced<'a> {
-
            Qualified::from_components(component!("reviews"), Component::from(patch), None)
+
        /// These references are not replicated or signed.
+
        pub mod draft {
+
            use super::*;
+

+
            /// Review draft reference. Points to the non-COB part of a patch review.
+
            ///
+
            /// `refs/namespaces/<remote>/refs/drafts/reviews/<patch-id>`
+
            ///
+
            /// When building a patch review, we store the intermediate state in this ref.
+
            pub fn review<'a>(remote: &RemoteId, patch: &cob::ObjectId) -> Namespaced<'a> {
+
                Qualified::from_components(
+
                    component!("drafts"),
+
                    component!("reviews"),
+
                    Some(Component::from(patch)),
+
                )
                .with_namespace(remote.into())
+
            }
+

+
            /// A draft collaborative object. This can also be a draft operation on an existing
+
            /// object.
+
            ///
+
            /// `refs/namespaces/<remote>/refs/drafts/cobs/<typename>/<object_id>`
+
            ///
+
            pub fn cob<'a>(
+
                remote: &RemoteId,
+
                typename: &cob::TypeName,
+
                object_id: &cob::ObjectId,
+
            ) -> Namespaced<'a> {
+
                Qualified::from_components(
+
                    component!("drafts"),
+
                    component!("cobs"),
+
                    [Component::from(typename), object_id.into()],
+
                )
+
                .with_namespace(remote.into())
+
            }
+

+
            /// Draft collaborative objects of a type.
+
            ///
+
            /// `refs/namespaces/<remote>/refs/drafts/cobs/<typename>/*`
+
            ///
+
            pub fn cobs(remote: &RemoteId, typename: &cob::TypeName) -> PatternString {
+
                Qualified::from_components(
+
                    component!("drafts"),
+
                    component!("cobs"),
+
                    Some(Component::from(typename)),
+
                )
+
                .with_namespace(remote.into())
+
                .to_pattern(refspec::pattern!("*"))
+
            }
        }

        /// Staging/temporary references.
modified radicle/src/storage/git/cob.rs
@@ -1,11 +1,13 @@
//! COB storage Git backend.
use std::collections::BTreeMap;

+
use cob::object::Objects;
use radicle_cob as cob;
use radicle_cob::change;

use crate::git;
use crate::storage::Error;
+
use crate::storage::ReadRepository;

pub use crate::git::*;
pub use cob::*;
@@ -161,3 +163,119 @@ impl cob::object::Storage for Repository {
        reference.delete().map_err(Self::RemoveError::from)
    }
}
+

+
/// Stores draft collaborative objects.
+
pub struct DraftStore {
+
    remote: RemoteId,
+
    repo: Repository,
+
}
+

+
impl cob::Store for DraftStore {}
+

+
impl change::Storage for DraftStore {
+
    type StoreError = <git2::Repository as change::Storage>::StoreError;
+
    type LoadError = <git2::Repository as change::Storage>::LoadError;
+

+
    type ObjectId = <git2::Repository as change::Storage>::ObjectId;
+
    type Parent = <git2::Repository as change::Storage>::Parent;
+
    type Signatures = <git2::Repository as change::Storage>::Signatures;
+

+
    fn store<Signer>(
+
        &self,
+
        authority: Self::Parent,
+
        parents: Vec<Self::Parent>,
+
        signer: &Signer,
+
        spec: change::Template<Self::ObjectId>,
+
    ) -> Result<cob::Change, Self::StoreError>
+
    where
+
        Signer: crypto::Signer,
+
    {
+
        self.repo.backend.store(authority, parents, signer, spec)
+
    }
+

+
    fn load(&self, id: Self::ObjectId) -> Result<cob::Change, Self::LoadError> {
+
        self.repo.backend.load(id)
+
    }
+

+
    fn parents_of(&self, id: &Oid) -> Result<Vec<Oid>, Self::LoadError> {
+
        self.repo.backend.parents_of(id)
+
    }
+
}
+

+
impl cob::object::Storage for DraftStore {
+
    type ObjectsError = ObjectsError;
+
    type TypesError = git::ext::Error;
+
    type UpdateError = git2::Error;
+
    type RemoveError = git2::Error;
+

+
    type Identifier = RemoteId;
+

+
    fn objects(
+
        &self,
+
        typename: &cob::TypeName,
+
        object_id: &cob::ObjectId,
+
    ) -> Result<cob::object::Objects, Self::ObjectsError> {
+
        // Nb. There can only be one draft per COB, per remote.
+
        let Ok(r) = self.repo.backend.find_reference(
+
            git::refs::storage::draft::cob(&self.remote, typename, object_id).as_str(),
+
        ) else {
+
            return Ok(Objects::default());
+
        };
+
        let r = cob::object::Reference::try_from(r).map_err(Self::ObjectsError::from)?;
+

+
        Ok(Objects::new(r))
+
    }
+

+
    fn types(
+
        &self,
+
        typename: &cob::TypeName,
+
    ) -> Result<BTreeMap<cob::ObjectId, cob::object::Objects>, Self::TypesError> {
+
        let glob = git::refs::storage::draft::cobs(&self.remote, typename);
+
        let references = self.repo.references_glob(&glob)?;
+
        let mut objs = BTreeMap::new();
+

+
        for (name, id) in references {
+
            let r = cob::object::Reference {
+
                name: name.into_refstring(),
+
                target: cob::object::Commit { id },
+
            };
+
            objs.insert(id.into(), Objects::new(r));
+
        }
+
        Ok(objs)
+
    }
+

+
    fn update(
+
        &self,
+
        identifier: &Self::Identifier,
+
        typename: &cob::TypeName,
+
        object_id: &cob::ObjectId,
+
        change: &cob::Change,
+
    ) -> Result<(), Self::UpdateError> {
+
        self.repo.backend.reference(
+
            git::refs::storage::draft::cob(identifier, typename, object_id).as_str(),
+
            (*change.id()).into(),
+
            true,
+
            &format!(
+
                "Updating draft collaborative object '{}/{}' with new change {}",
+
                typename,
+
                object_id,
+
                change.id()
+
            ),
+
        )?;
+

+
        Ok(())
+
    }
+

+
    fn remove(
+
        &self,
+
        identifier: &Self::Identifier,
+
        typename: &cob::TypeName,
+
        object_id: &cob::ObjectId,
+
    ) -> Result<(), Self::RemoveError> {
+
        let mut reference = self.repo.backend.find_reference(
+
            git::refs::storage::draft::cob(identifier, typename, object_id).as_str(),
+
        )?;
+

+
        reference.delete().map_err(Self::RemoveError::from)
+
    }
+
}