Tidy up COBs code a little
* Move storage impl into `storage:
: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:
parent
bf22cea014
commit
49cd26c0b3
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue