diff --git a/radicle-cob/src/backend/git/change.rs b/radicle-cob/src/backend/git/change.rs index a081e224..c445c60e 100644 --- a/radicle-cob/src/backend/git/change.rs +++ b/radicle-cob/src/backend/git/change.rs @@ -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( + fn store( &self, resource: Self::Resource, signer: &Signer, - spec: store::Create, - ) -> Result + spec: store::Template, + ) -> Result where Signer: crypto::Signer, { - let change::Create { + let change::Template { typename, history_type, tips, diff --git a/radicle-cob/src/backend/git/objects.rs b/radicle-cob/src/backend/git/objects.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/radicle-cob/src/change.rs b/radicle-cob/src/change.rs index 9252cad6..aa64059b 100644 --- a/radicle-cob/src/change.rs +++ b/radicle-cob/src/change.rs @@ -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; diff --git a/radicle-cob/src/change/store.rs b/radicle-cob/src/change/store.rs index da0dae6d..750ccc60 100644 --- a/radicle-cob/src/change/store.rs +++ b/radicle-cob/src/change/store.rs @@ -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( + fn store( &self, authority: Self::Resource, - signer: &Signer, - spec: Create, - ) -> Result, Self::CreateError> + signer: &G, + template: Template, + ) -> Result, 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, Self::LoadError>; } -pub struct Create { +/// Change template, used to create a new change. +pub struct Template { pub typename: TypeName, pub history_type: String, pub tips: Vec, diff --git a/radicle-cob/src/lib.rs b/radicle-cob/src/lib.rs index 2ff6b891..f18465bf 100644 --- a/radicle-cob/src/lib.rs +++ b/radicle-cob/src/lib.rs @@ -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, diff --git a/radicle-cob/src/object/collaboration/create.rs b/radicle-cob/src/object/collaboration/create.rs index 2dddd714..58c047b1 100644 --- a/radicle-cob/src/object/collaboration/create.rs +++ b/radicle-cob/src/object/collaboration/create.rs @@ -20,8 +20,8 @@ pub struct Create { } impl Create { - fn create_spec(&self) -> change::Create { - change::Create { + fn template(&self) -> change::Template { + 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( diff --git a/radicle-cob/src/object/collaboration/update.rs b/radicle-cob/src/object/collaboration/update.rs index e9943949..1eea666c 100644 --- a/radicle-cob/src/object/collaboration/update.rs +++ b/radicle-cob/src/object/collaboration/update.rs @@ -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(), diff --git a/radicle-cob/src/test/storage.rs b/radicle-cob/src/test/storage.rs index 2178bd08..69faef97 100644 --- a/radicle-cob/src/test/storage.rs +++ b/radicle-cob/src/test/storage.rs @@ -59,26 +59,26 @@ impl Storage { impl Store for Storage {} impl change::Storage for Storage { - type CreateError = ::CreateError; + type StoreError = ::StoreError; type LoadError = ::LoadError; type ObjectId = ::ObjectId; type Resource = ::Resource; type Signatures = ::Signatures; - fn create( + fn store( &self, authority: Self::Resource, signer: &Signer, - spec: change::Create, + spec: change::Template, ) -> Result< change::store::Change, - Self::CreateError, + Self::StoreError, > where Signer: crypto::Signer, { - self.as_raw().create(authority, signer, spec) + self.as_raw().store(authority, signer, spec) } fn load( diff --git a/radicle/src/storage/git.rs b/radicle/src/storage/git.rs index beb9c641..a219a7b6 100644 --- a/radicle/src/storage/git.rs +++ b/radicle/src/storage/git.rs @@ -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 = ::CreateError; - type LoadError = ::LoadError; - - type ObjectId = ::ObjectId; - type Resource = ::Resource; - type Signatures = ::Signatures; - - fn create( - &self, - authority: Self::Resource, - signer: &Signer, - spec: change::Create, - ) -> Result - where - Signer: crypto::Signer, - { - self.backend.create(authority, signer, spec) - } - - fn load(&self, id: Self::ObjectId) -> Result { - 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 { - 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::, _>>()?; - Ok(refs.into()) - } - - fn types( - &self, - typename: &cob::TypeName, - ) -> Result, 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; diff --git a/radicle/src/storage/git/cob.rs b/radicle/src/storage/git/cob.rs new file mode 100644 index 00000000..8cebc3df --- /dev/null +++ b/radicle/src/storage/git/cob.rs @@ -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 = ::StoreError; + type LoadError = ::LoadError; + + type ObjectId = ::ObjectId; + type Resource = ::Resource; + type Signatures = ::Signatures; + + fn store( + &self, + authority: Self::Resource, + signer: &Signer, + spec: change::Template, + ) -> Result + where + Signer: crypto::Signer, + { + self.backend.store(authority, signer, spec) + } + + fn load(&self, id: Self::ObjectId) -> Result { + 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 { + 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::, _>>()?; + Ok(refs.into()) + } + + fn types( + &self, + typename: &cob::TypeName, + ) -> Result, 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) + } +}