radicle: Introduce `cob::DraftStore`
This storage backend for COBs stores changes in a `draft/cobs/*` namespace, which allows for some of the features needed for code review. For example, users can draft comments and later decide to publish them.
This commit is contained in:
parent
c9162121b0
commit
ff7e9ded17
|
|
@ -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()?,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue