Tidy up COBs code a little

* Move storage impl into `storage::git::cob`
* Rename `change::Create` to `change::Template`
* Rename `change::Storage::create` to `change::Storage::store`

Some of the renames are to further distinguish COBs from Changes, and
the different types of "create" operations within cobs.

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-12-16 15:12:30 +01:00
parent bf22cea014
commit 49cd26c0b3
No known key found for this signature in database
10 changed files with 186 additions and 169 deletions

View File

@ -80,23 +80,23 @@ pub mod error {
}
impl change::Storage for git2::Repository {
type CreateError = error::Create;
type StoreError = error::Create;
type LoadError = error::Load;
type ObjectId = Oid;
type Resource = Oid;
type Signatures = Signature;
fn create<Signer>(
fn store<Signer>(
&self,
resource: Self::Resource,
signer: &Signer,
spec: store::Create<Self::ObjectId>,
) -> Result<Change, Self::CreateError>
spec: store::Template<Self::ObjectId>,
) -> Result<Change, Self::StoreError>
where
Signer: crypto::Signer,
{
let change::Create {
let change::Template {
typename,
history_type,
tips,

View File

@ -6,7 +6,7 @@
use git_ext::Oid;
pub mod store;
pub use store::{Create, Storage};
pub use store::{Storage, Template};
use crate::signatures::Signature;

View File

@ -12,24 +12,27 @@ use crate::{
signatures, TypeName,
};
/// Change storage.
pub trait Storage {
type CreateError: Error + Send + Sync + 'static;
type StoreError: Error + Send + Sync + 'static;
type LoadError: Error + Send + Sync + 'static;
type ObjectId;
type Resource;
type Signatures;
/// Store a new change.
#[allow(clippy::type_complexity)]
fn create<Signer>(
fn store<G>(
&self,
authority: Self::Resource,
signer: &Signer,
spec: Create<Self::ObjectId>,
) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::CreateError>
signer: &G,
template: Template<Self::ObjectId>,
) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::StoreError>
where
Signer: crypto::Signer;
G: crypto::Signer;
/// Load a change.
#[allow(clippy::type_complexity)]
fn load(
&self,
@ -37,7 +40,8 @@ pub trait Storage {
) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::LoadError>;
}
pub struct Create<Id> {
/// Change template, used to create a new change.
pub struct Template<Id> {
pub typename: TypeName,
pub history_type: String,
pub tips: Vec<Id>,

View File

@ -131,7 +131,7 @@ pub trait Store
where
Self: object::Storage
+ change::Storage<
CreateError = git::change::error::Create,
StoreError = git::change::error::Create,
LoadError = git::change::error::Load,
ObjectId = git_ext::Oid,
Resource = git_ext::Oid,

View File

@ -20,8 +20,8 @@ pub struct Create {
}
impl Create {
fn create_spec(&self) -> change::Create<git_ext::Oid> {
change::Create {
fn template(&self) -> change::Template<git_ext::Oid> {
change::Template {
typename: self.typename.clone(),
history_type: self.history_type.clone(),
tips: Vec::new(),
@ -68,7 +68,7 @@ where
} = &args;
let init_change = storage
.create(resource.content_id(), signer, args.create_spec())
.store(resource.content_id(), signer, args.template())
.map_err(error::Create::from)?;
let history = History::new_from_root(

View File

@ -70,10 +70,10 @@ where
.map(|graph| graph.evaluate())
.ok_or(error::Update::NoSuchObject)?;
let change = storage.create(
let change = storage.store(
resource.content_id(),
signer,
change::Create {
change::Template {
tips: object.tips().iter().cloned().collect(),
history_type,
contents: changes.clone(),

View File

@ -59,26 +59,26 @@ impl Storage {
impl Store for Storage {}
impl change::Storage for Storage {
type CreateError = <git2::Repository as change::Storage>::CreateError;
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 Resource = <git2::Repository as change::Storage>::Resource;
type Signatures = <git2::Repository as change::Storage>::Signatures;
fn create<Signer>(
fn store<Signer>(
&self,
authority: Self::Resource,
signer: &Signer,
spec: change::Create<Self::ObjectId>,
spec: change::Template<Self::ObjectId>,
) -> Result<
change::store::Change<Self::Resource, Self::ObjectId, Self::Signatures>,
Self::CreateError,
Self::StoreError,
>
where
Signer: crypto::Signer,
{
self.as_raw().create(authority, signer, spec)
self.as_raw().store(authority, signer, spec)
}
fn load(

View File

@ -1,3 +1,4 @@
pub mod cob;
pub mod transport;
use std::collections::{BTreeMap, HashMap};
@ -7,7 +8,6 @@ use std::{fs, io};
use crypto::{Signer, Unverified, Verified};
use git_ref_format::refspec;
use once_cell::sync::Lazy;
use radicle_cob::{self as cob, change};
use crate::git;
use crate::identity;
@ -61,28 +61,6 @@ impl ProjectError {
}
}
#[derive(Error, Debug)]
pub enum CobObjectsError {
#[error(transparent)]
Convert(#[from] cob::object::storage::convert::Error),
#[error(transparent)]
Git(#[from] git2::Error),
}
#[derive(Error, Debug)]
pub enum CobTypesError {
#[error(transparent)]
Convert(#[from] cob::object::storage::convert::Error),
#[error(transparent)]
Git(#[from] git2::Error),
#[error(transparent)]
ParseKey(#[from] crypto::Error),
#[error(transparent)]
ParseObjectId(#[from] cob::object::ParseObjectId),
#[error(transparent)]
RefFormat(#[from] git_ref_format::Error),
}
#[derive(Debug, Clone)]
pub struct Storage {
path: PathBuf,
@ -699,128 +677,6 @@ impl WriteRepository for Repository {
}
}
impl cob::Store for Repository {}
impl change::Storage for Repository {
type CreateError = <git2::Repository as change::Storage>::CreateError;
type LoadError = <git2::Repository as change::Storage>::LoadError;
type ObjectId = <git2::Repository as change::Storage>::ObjectId;
type Resource = <git2::Repository as change::Storage>::Resource;
type Signatures = <git2::Repository as change::Storage>::Signatures;
fn create<Signer>(
&self,
authority: Self::Resource,
signer: &Signer,
spec: change::Create<Self::ObjectId>,
) -> Result<cob::Change, Self::CreateError>
where
Signer: crypto::Signer,
{
self.backend.create(authority, signer, spec)
}
fn load(&self, id: Self::ObjectId) -> Result<cob::Change, Self::LoadError> {
self.backend.load(id)
}
}
impl cob::object::Storage for Repository {
type ObjectsError = CobObjectsError;
type TypesError = CobTypesError;
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> {
let refs = self
.backend
.references_glob(git::refs::storage::cobs(typename, object_id).as_str())?;
let refs = refs
.map(|r| {
r.map_err(Self::ObjectsError::from).and_then(|r| {
cob::object::Reference::try_from(r).map_err(Self::ObjectsError::from)
})
})
.collect::<Result<Vec<_>, _>>()?;
Ok(refs.into())
}
fn types(
&self,
typename: &cob::TypeName,
) -> Result<HashMap<cob::ObjectId, cob::object::Objects>, Self::TypesError> {
let mut references = self.backend.references()?.filter_map(|reference| {
let reference = reference.ok()?;
match RefStr::try_from_str(reference.name()?) {
Ok(name) => {
let (ty, object_id) = cob::object::parse_refstr(&name)?;
if ty == *typename {
Some(
cob::object::Reference::try_from(reference)
.map_err(Self::TypesError::from)
.map(|reference| (object_id, reference)),
)
} else {
None
}
}
Err(err) => Some(Err(err.into())),
}
});
references.try_fold(HashMap::new(), |mut objects, result| {
let (oid, reference) = result?;
objects
.entry(oid)
.and_modify(|objs: &mut cob::object::Objects| objs.push(reference.clone()))
.or_insert_with(|| cob::object::Objects::new(reference));
Ok(objects)
})
}
fn update(
&self,
identifier: &Self::Identifier,
typename: &cob::TypeName,
object_id: &cob::ObjectId,
change: &cob::Change,
) -> Result<(), Self::UpdateError> {
self.backend.reference(
git::refs::storage::cob(identifier, typename, object_id).as_str(),
(*change.id()).into(),
true,
&format!(
"Updating 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
.backend
.find_reference(git::refs::storage::cob(identifier, typename, object_id).as_str())?;
reference.delete().map_err(Self::RemoveError::from)
}
}
pub mod trailers {
use std::str::FromStr;

View File

@ -0,0 +1,157 @@
//! COB storage Git backend.
use std::collections::HashMap;
use radicle_cob as cob;
use radicle_cob::change;
use crate::git;
use crate::storage::Error;
pub use crate::git::*;
pub use cob::*;
use super::{RemoteId, Repository};
#[derive(Error, Debug)]
pub enum ObjectsError {
#[error(transparent)]
Convert(#[from] cob::object::storage::convert::Error),
#[error(transparent)]
Git(#[from] git2::Error),
}
#[derive(Error, Debug)]
pub enum TypesError {
#[error(transparent)]
Convert(#[from] cob::object::storage::convert::Error),
#[error(transparent)]
Git(#[from] git2::Error),
#[error(transparent)]
ParseKey(#[from] crypto::Error),
#[error(transparent)]
ParseObjectId(#[from] cob::object::ParseObjectId),
#[error(transparent)]
RefFormat(#[from] git_ref_format::Error),
}
impl cob::Store for Repository {}
impl change::Storage for Repository {
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 Resource = <git2::Repository as change::Storage>::Resource;
type Signatures = <git2::Repository as change::Storage>::Signatures;
fn store<Signer>(
&self,
authority: Self::Resource,
signer: &Signer,
spec: change::Template<Self::ObjectId>,
) -> Result<cob::Change, Self::StoreError>
where
Signer: crypto::Signer,
{
self.backend.store(authority, signer, spec)
}
fn load(&self, id: Self::ObjectId) -> Result<cob::Change, Self::LoadError> {
self.backend.load(id)
}
}
impl cob::object::Storage for Repository {
type ObjectsError = ObjectsError;
type TypesError = TypesError;
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> {
let refs = self
.backend
.references_glob(git::refs::storage::cobs(typename, object_id).as_str())?;
let refs = refs
.map(|r| {
r.map_err(Self::ObjectsError::from).and_then(|r| {
cob::object::Reference::try_from(r).map_err(Self::ObjectsError::from)
})
})
.collect::<Result<Vec<_>, _>>()?;
Ok(refs.into())
}
fn types(
&self,
typename: &cob::TypeName,
) -> Result<HashMap<cob::ObjectId, cob::object::Objects>, Self::TypesError> {
let mut references = self.backend.references()?.filter_map(|reference| {
let reference = reference.ok()?;
match RefStr::try_from_str(reference.name()?) {
Ok(name) => {
let (ty, object_id) = cob::object::parse_refstr(&name)?;
if ty == *typename {
Some(
cob::object::Reference::try_from(reference)
.map_err(Self::TypesError::from)
.map(|reference| (object_id, reference)),
)
} else {
None
}
}
Err(err) => Some(Err(err.into())),
}
});
references.try_fold(HashMap::new(), |mut objects, result| {
let (oid, reference) = result?;
objects
.entry(oid)
.and_modify(|objs: &mut cob::object::Objects| objs.push(reference.clone()))
.or_insert_with(|| cob::object::Objects::new(reference));
Ok(objects)
})
}
fn update(
&self,
identifier: &Self::Identifier,
typename: &cob::TypeName,
object_id: &cob::ObjectId,
change: &cob::Change,
) -> Result<(), Self::UpdateError> {
self.backend.reference(
git::refs::storage::cob(identifier, typename, object_id).as_str(),
(*change.id()).into(),
true,
&format!(
"Updating 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
.backend
.find_reference(git::refs::storage::cob(identifier, typename, object_id).as_str())?;
reference.delete().map_err(Self::RemoveError::from)
}
}