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 {
|
impl change::Storage for git2::Repository {
|
||||||
type CreateError = error::Create;
|
type StoreError = error::Create;
|
||||||
type LoadError = error::Load;
|
type LoadError = error::Load;
|
||||||
|
|
||||||
type ObjectId = Oid;
|
type ObjectId = Oid;
|
||||||
type Resource = Oid;
|
type Resource = Oid;
|
||||||
type Signatures = Signature;
|
type Signatures = Signature;
|
||||||
|
|
||||||
fn create<Signer>(
|
fn store<Signer>(
|
||||||
&self,
|
&self,
|
||||||
resource: Self::Resource,
|
resource: Self::Resource,
|
||||||
signer: &Signer,
|
signer: &Signer,
|
||||||
spec: store::Create<Self::ObjectId>,
|
spec: store::Template<Self::ObjectId>,
|
||||||
) -> Result<Change, Self::CreateError>
|
) -> Result<Change, Self::StoreError>
|
||||||
where
|
where
|
||||||
Signer: crypto::Signer,
|
Signer: crypto::Signer,
|
||||||
{
|
{
|
||||||
let change::Create {
|
let change::Template {
|
||||||
typename,
|
typename,
|
||||||
history_type,
|
history_type,
|
||||||
tips,
|
tips,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
use git_ext::Oid;
|
use git_ext::Oid;
|
||||||
|
|
||||||
pub mod store;
|
pub mod store;
|
||||||
pub use store::{Create, Storage};
|
pub use store::{Storage, Template};
|
||||||
|
|
||||||
use crate::signatures::Signature;
|
use crate::signatures::Signature;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,24 +12,27 @@ use crate::{
|
||||||
signatures, TypeName,
|
signatures, TypeName,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Change storage.
|
||||||
pub trait Storage {
|
pub trait Storage {
|
||||||
type CreateError: Error + Send + Sync + 'static;
|
type StoreError: Error + Send + Sync + 'static;
|
||||||
type LoadError: Error + Send + Sync + 'static;
|
type LoadError: Error + Send + Sync + 'static;
|
||||||
|
|
||||||
type ObjectId;
|
type ObjectId;
|
||||||
type Resource;
|
type Resource;
|
||||||
type Signatures;
|
type Signatures;
|
||||||
|
|
||||||
|
/// Store a new change.
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn create<Signer>(
|
fn store<G>(
|
||||||
&self,
|
&self,
|
||||||
authority: Self::Resource,
|
authority: Self::Resource,
|
||||||
signer: &Signer,
|
signer: &G,
|
||||||
spec: Create<Self::ObjectId>,
|
template: Template<Self::ObjectId>,
|
||||||
) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::CreateError>
|
) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::StoreError>
|
||||||
where
|
where
|
||||||
Signer: crypto::Signer;
|
G: crypto::Signer;
|
||||||
|
|
||||||
|
/// Load a change.
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn load(
|
fn load(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -37,7 +40,8 @@ pub trait Storage {
|
||||||
) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::LoadError>;
|
) -> 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 typename: TypeName,
|
||||||
pub history_type: String,
|
pub history_type: String,
|
||||||
pub tips: Vec<Id>,
|
pub tips: Vec<Id>,
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ pub trait Store
|
||||||
where
|
where
|
||||||
Self: object::Storage
|
Self: object::Storage
|
||||||
+ change::Storage<
|
+ change::Storage<
|
||||||
CreateError = git::change::error::Create,
|
StoreError = git::change::error::Create,
|
||||||
LoadError = git::change::error::Load,
|
LoadError = git::change::error::Load,
|
||||||
ObjectId = git_ext::Oid,
|
ObjectId = git_ext::Oid,
|
||||||
Resource = git_ext::Oid,
|
Resource = git_ext::Oid,
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ pub struct Create {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Create {
|
impl Create {
|
||||||
fn create_spec(&self) -> change::Create<git_ext::Oid> {
|
fn template(&self) -> change::Template<git_ext::Oid> {
|
||||||
change::Create {
|
change::Template {
|
||||||
typename: self.typename.clone(),
|
typename: self.typename.clone(),
|
||||||
history_type: self.history_type.clone(),
|
history_type: self.history_type.clone(),
|
||||||
tips: Vec::new(),
|
tips: Vec::new(),
|
||||||
|
|
@ -68,7 +68,7 @@ where
|
||||||
} = &args;
|
} = &args;
|
||||||
|
|
||||||
let init_change = storage
|
let init_change = storage
|
||||||
.create(resource.content_id(), signer, args.create_spec())
|
.store(resource.content_id(), signer, args.template())
|
||||||
.map_err(error::Create::from)?;
|
.map_err(error::Create::from)?;
|
||||||
|
|
||||||
let history = History::new_from_root(
|
let history = History::new_from_root(
|
||||||
|
|
|
||||||
|
|
@ -70,10 +70,10 @@ where
|
||||||
.map(|graph| graph.evaluate())
|
.map(|graph| graph.evaluate())
|
||||||
.ok_or(error::Update::NoSuchObject)?;
|
.ok_or(error::Update::NoSuchObject)?;
|
||||||
|
|
||||||
let change = storage.create(
|
let change = storage.store(
|
||||||
resource.content_id(),
|
resource.content_id(),
|
||||||
signer,
|
signer,
|
||||||
change::Create {
|
change::Template {
|
||||||
tips: object.tips().iter().cloned().collect(),
|
tips: object.tips().iter().cloned().collect(),
|
||||||
history_type,
|
history_type,
|
||||||
contents: changes.clone(),
|
contents: changes.clone(),
|
||||||
|
|
|
||||||
|
|
@ -59,26 +59,26 @@ impl Storage {
|
||||||
impl Store for Storage {}
|
impl Store for Storage {}
|
||||||
|
|
||||||
impl change::Storage 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 LoadError = <git2::Repository as change::Storage>::LoadError;
|
||||||
|
|
||||||
type ObjectId = <git2::Repository as change::Storage>::ObjectId;
|
type ObjectId = <git2::Repository as change::Storage>::ObjectId;
|
||||||
type Resource = <git2::Repository as change::Storage>::Resource;
|
type Resource = <git2::Repository as change::Storage>::Resource;
|
||||||
type Signatures = <git2::Repository as change::Storage>::Signatures;
|
type Signatures = <git2::Repository as change::Storage>::Signatures;
|
||||||
|
|
||||||
fn create<Signer>(
|
fn store<Signer>(
|
||||||
&self,
|
&self,
|
||||||
authority: Self::Resource,
|
authority: Self::Resource,
|
||||||
signer: &Signer,
|
signer: &Signer,
|
||||||
spec: change::Create<Self::ObjectId>,
|
spec: change::Template<Self::ObjectId>,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
change::store::Change<Self::Resource, Self::ObjectId, Self::Signatures>,
|
change::store::Change<Self::Resource, Self::ObjectId, Self::Signatures>,
|
||||||
Self::CreateError,
|
Self::StoreError,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
Signer: crypto::Signer,
|
Signer: crypto::Signer,
|
||||||
{
|
{
|
||||||
self.as_raw().create(authority, signer, spec)
|
self.as_raw().store(authority, signer, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(
|
fn load(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod cob;
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
@ -7,7 +8,6 @@ use std::{fs, io};
|
||||||
use crypto::{Signer, Unverified, Verified};
|
use crypto::{Signer, Unverified, Verified};
|
||||||
use git_ref_format::refspec;
|
use git_ref_format::refspec;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use radicle_cob::{self as cob, change};
|
|
||||||
|
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::identity;
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Storage {
|
pub struct Storage {
|
||||||
path: PathBuf,
|
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 {
|
pub mod trailers {
|
||||||
use std::str::FromStr;
|
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