node: Turn remotes into an iterator
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
parent
078184b7d6
commit
7be6fe3414
|
|
@ -24,7 +24,7 @@ use crate::protocol::config::ProjectTracking;
|
|||
use crate::protocol::message::Message;
|
||||
use crate::protocol::peer::{Peer, PeerError, PeerState};
|
||||
use crate::storage::{self, ReadRepository, WriteRepository};
|
||||
use crate::storage::{Inventory, ReadStorage, WriteStorage};
|
||||
use crate::storage::{Inventory, WriteStorage};
|
||||
|
||||
pub use crate::protocol::config::{Config, Network};
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ pub struct Protocol<S, T, G> {
|
|||
start_time: LocalTime,
|
||||
}
|
||||
|
||||
impl<T: ReadStorage + WriteStorage, S: address_book::Store, G: crypto::Signer> Protocol<S, T, G> {
|
||||
impl<'r, T: WriteStorage<'r>, S: address_book::Store, G: crypto::Signer> Protocol<S, T, G> {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
clock: RefClock,
|
||||
|
|
@ -253,9 +253,9 @@ impl<T: ReadStorage + WriteStorage, S: address_book::Store, G: crypto::Signer> P
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, T, G> nakamoto::Protocol for Protocol<S, T, G>
|
||||
impl<'r, S, T, G> nakamoto::Protocol for Protocol<S, T, G>
|
||||
where
|
||||
T: ReadStorage + WriteStorage + 'static,
|
||||
T: WriteStorage<'r> + 'static,
|
||||
S: address_book::Store,
|
||||
G: crypto::Signer,
|
||||
{
|
||||
|
|
@ -572,9 +572,9 @@ pub struct Context<S, T, G> {
|
|||
rng: Rng,
|
||||
}
|
||||
|
||||
impl<S, T, G> Context<S, T, G>
|
||||
impl<'r, S, T, G> Context<S, T, G>
|
||||
where
|
||||
T: storage::ReadStorage + storage::WriteStorage,
|
||||
T: storage::WriteStorage<'r>,
|
||||
G: crypto::Signer,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
|
|
|
|||
|
|
@ -95,13 +95,13 @@ impl Peer {
|
|||
self.attempts = 0;
|
||||
}
|
||||
|
||||
pub fn received<S, T, G>(
|
||||
pub fn received<'r, S, T, G>(
|
||||
&mut self,
|
||||
envelope: Envelope,
|
||||
ctx: &mut Context<S, T, G>,
|
||||
) -> Result<Option<Message>, PeerError>
|
||||
where
|
||||
T: storage::ReadStorage + storage::WriteStorage,
|
||||
T: storage::WriteStorage<'r>,
|
||||
G: crypto::Signer,
|
||||
{
|
||||
if envelope.magic != ctx.config.network.magic() {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ pub enum InitError {
|
|||
}
|
||||
|
||||
/// Initialize a new radicle project from a git repository.
|
||||
pub fn init<S: storage::WriteStorage>(
|
||||
pub fn init<'r, S: storage::WriteStorage<'r>>(
|
||||
repo: &git2::Repository,
|
||||
name: &str,
|
||||
description: &str,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,12 @@ pub type RemoteId = UserId;
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Remotes<V>(HashMap<RemoteId, Remote<V>>);
|
||||
|
||||
impl<V> FromIterator<(RemoteId, Remote<V>)> for Remotes<V> {
|
||||
fn from_iter<T: IntoIterator<Item = (RemoteId, Remote<V>)>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Deref for Remotes<V> {
|
||||
type Target = HashMap<RemoteId, Remote<V>>;
|
||||
|
||||
|
|
@ -159,14 +165,17 @@ pub trait ReadStorage {
|
|||
fn inventory(&self) -> Result<Inventory, Error>;
|
||||
}
|
||||
|
||||
pub trait WriteStorage: ReadStorage {
|
||||
type Repository: WriteRepository;
|
||||
pub trait WriteStorage<'r>: ReadStorage {
|
||||
type Repository: WriteRepository<'r>;
|
||||
|
||||
fn repository(&self, proj: &ProjId) -> Result<Self::Repository, Error>;
|
||||
fn sign_refs(&self, repository: &Self::Repository) -> Result<SignedRefs<Verified>, Error>;
|
||||
}
|
||||
|
||||
pub trait ReadRepository {
|
||||
pub trait ReadRepository<'r> {
|
||||
type Remotes: Iterator<Item = Result<(RemoteId, Remote<Verified>), refs::Error>> + 'r;
|
||||
|
||||
fn is_empty(&self) -> Result<bool, git2::Error>;
|
||||
fn path(&self) -> &Path;
|
||||
fn blob_at<'a>(&'a self, oid: Oid, path: &'a Path) -> Result<git2::Blob<'a>, git_ext::Error>;
|
||||
fn reference(
|
||||
|
|
@ -177,10 +186,10 @@ pub trait ReadRepository {
|
|||
fn reference_oid(&self, user: &UserId, reference: &RefStr) -> Result<Option<Oid>, git2::Error>;
|
||||
fn references(&self, user: &UserId) -> Result<Refs, Error>;
|
||||
fn remote(&self, user: &UserId) -> Result<Remote<Verified>, refs::Error>;
|
||||
fn remotes(&self) -> Result<Remotes<Verified>, refs::Error>;
|
||||
fn remotes(&'r self) -> Result<Self::Remotes, git2::Error>;
|
||||
}
|
||||
|
||||
pub trait WriteRepository: ReadRepository {
|
||||
pub trait WriteRepository<'r>: ReadRepository<'r> {
|
||||
fn fetch(&mut self, url: &Url) -> Result<(), git2::Error>;
|
||||
fn raw(&self) -> &git2::Repository;
|
||||
}
|
||||
|
|
@ -207,10 +216,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T, S> WriteStorage for T
|
||||
impl<'r, T, S> WriteStorage<'r> for T
|
||||
where
|
||||
T: DerefMut<Target = S>,
|
||||
S: WriteStorage + 'static,
|
||||
S: WriteStorage<'r> + 'static,
|
||||
{
|
||||
type Repository = S::Repository;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use once_cell::sync::Lazy;
|
|||
|
||||
pub use radicle_git_ext::Oid;
|
||||
|
||||
use crate::collections::HashMap;
|
||||
use crate::crypto::{Signer, Verified};
|
||||
use crate::git;
|
||||
use crate::identity::{self, IDENTITY_PATH};
|
||||
|
|
@ -16,7 +15,7 @@ use crate::identity::{ProjId, Project, UserId};
|
|||
use crate::storage::refs;
|
||||
use crate::storage::refs::{Refs, SignedRefs};
|
||||
use crate::storage::{
|
||||
Error, Inventory, ReadRepository, ReadStorage, Remote, Remotes, WriteRepository, WriteStorage,
|
||||
Error, Inventory, ReadRepository, ReadStorage, Remote, WriteRepository, WriteStorage,
|
||||
};
|
||||
|
||||
use super::RemoteId;
|
||||
|
|
@ -58,7 +57,7 @@ impl ReadStorage for Storage {
|
|||
let repo = self.repository(id)?;
|
||||
|
||||
if let Some(doc) = repo.identity(local)? {
|
||||
let remotes = repo.remotes()?;
|
||||
let remotes = repo.remotes()?.collect::<Result<_, _>>()?;
|
||||
|
||||
// TODO: We should check that there is at least one remote, which is
|
||||
// the one of the local user, otherwise it means the project is in
|
||||
|
|
@ -79,7 +78,7 @@ impl ReadStorage for Storage {
|
|||
}
|
||||
}
|
||||
|
||||
impl WriteStorage for Storage {
|
||||
impl<'r> WriteStorage<'r> for Storage {
|
||||
type Repository = Repository;
|
||||
|
||||
fn repository(&self, proj: &ProjId) -> Result<Self::Repository, Error> {
|
||||
|
|
@ -221,7 +220,14 @@ impl Repository {
|
|||
}
|
||||
}
|
||||
|
||||
impl ReadRepository for Repository {
|
||||
impl<'r> ReadRepository<'r> for Repository {
|
||||
type Remotes = Box<dyn Iterator<Item = Result<(RemoteId, Remote<Verified>), refs::Error>> + 'r>;
|
||||
|
||||
fn is_empty(&self) -> Result<bool, git2::Error> {
|
||||
let some = self.remotes()?.next().is_some();
|
||||
Ok(!some)
|
||||
}
|
||||
|
||||
fn path(&self) -> &Path {
|
||||
self.backend.path()
|
||||
}
|
||||
|
|
@ -281,24 +287,23 @@ impl ReadRepository for Repository {
|
|||
Ok(refs.into())
|
||||
}
|
||||
|
||||
fn remotes(&self) -> Result<Remotes<Verified>, refs::Error> {
|
||||
// TODO: Should use the id glob here instead of signature.
|
||||
let refs = self.backend.references_glob(SIGNATURES_GLOB.as_str())?;
|
||||
let mut remotes = HashMap::default();
|
||||
fn remotes(&'r self) -> Result<Self::Remotes, git2::Error> {
|
||||
let iter = self.backend.references_glob(SIGNATURES_GLOB.as_str())?.map(
|
||||
|reference| -> Result<(RemoteId, Remote<Verified>), refs::Error> {
|
||||
let r = reference?;
|
||||
let name = r.name().ok_or(refs::Error::InvalidRef)?;
|
||||
let (id, _) = git::parse_ref::<RemoteId>(name)?;
|
||||
let remote = self.remote(&id)?;
|
||||
|
||||
for r in refs {
|
||||
let r = r?;
|
||||
let name = r.name().ok_or(refs::Error::InvalidRef)?;
|
||||
let (id, _) = git::parse_ref::<RemoteId>(name)?;
|
||||
let remote = self.remote(&id)?;
|
||||
Ok((id, remote))
|
||||
},
|
||||
);
|
||||
|
||||
remotes.insert(id, remote);
|
||||
}
|
||||
Ok(Remotes::new(remotes))
|
||||
Ok(Box::new(iter))
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteRepository for Repository {
|
||||
impl<'r> WriteRepository<'r> for Repository {
|
||||
/// Fetch all remotes of a project from the given URL.
|
||||
fn fetch(&mut self, url: &git::Url) -> Result<(), git2::Error> {
|
||||
// TODO: Have function to fetch specific remotes.
|
||||
|
|
@ -367,7 +372,13 @@ mod tests {
|
|||
for remote in refs.values_mut() {
|
||||
remote.remove(&*SIGNATURE_REF).unwrap();
|
||||
}
|
||||
assert_eq!(refs, remotes.into());
|
||||
|
||||
let remotes = remotes
|
||||
.map(|remote| remote.map(|(id, r): (RemoteId, Remote<Verified>)| (id, r.refs.into())))
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(refs, remotes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -377,7 +388,8 @@ mod tests {
|
|||
let bob = Storage::open(tmp.path().join("bob"), MockSigner::default()).unwrap();
|
||||
let inventory = alice.inventory().unwrap();
|
||||
let proj = inventory.first().unwrap();
|
||||
let remotes = alice.repository(proj).unwrap().remotes().unwrap();
|
||||
let repo = alice.repository(proj).unwrap();
|
||||
let remotes = repo.remotes().unwrap();
|
||||
let refname = git::refname!("heads/master");
|
||||
|
||||
// Have Bob fetch Alice's refs.
|
||||
|
|
@ -391,7 +403,8 @@ mod tests {
|
|||
})
|
||||
.unwrap();
|
||||
|
||||
for (id, _) in remotes.into_iter() {
|
||||
for remote in remotes {
|
||||
let (id, _) = remote.unwrap();
|
||||
let alice_repo = alice.repository(proj).unwrap();
|
||||
let alice_oid = alice_repo.reference(&id, &refname).unwrap().unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -193,9 +193,9 @@ impl SignedRefs<Unverified> {
|
|||
}
|
||||
|
||||
impl SignedRefs<Verified> {
|
||||
pub fn load<S>(remote: &RemoteId, repo: &S) -> Result<Self, Error>
|
||||
pub fn load<'r, S>(remote: &RemoteId, repo: &S) -> Result<Self, Error>
|
||||
where
|
||||
S: ReadRepository,
|
||||
S: ReadRepository<'r>,
|
||||
{
|
||||
if let Some(oid) = repo.reference_oid(remote, &SIGNATURE_REF)? {
|
||||
Self::load_at(oid, remote, repo)
|
||||
|
|
@ -204,9 +204,9 @@ impl SignedRefs<Verified> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_at<S>(oid: Oid, remote: &RemoteId, repo: &S) -> Result<Self, Error>
|
||||
pub fn load_at<'r, S>(oid: Oid, remote: &RemoteId, repo: &S) -> Result<Self, Error>
|
||||
where
|
||||
S: storage::ReadRepository,
|
||||
S: storage::ReadRepository<'r>,
|
||||
{
|
||||
let refs = repo.blob_at(oid, Path::new(REFS_BLOB_PATH))?;
|
||||
let signature = repo.blob_at(oid, Path::new(SIGNATURE_BLOB_PATH))?;
|
||||
|
|
@ -228,7 +228,7 @@ impl SignedRefs<Verified> {
|
|||
|
||||
/// Save the signed refs to disk.
|
||||
/// This creates a new commit on the signed refs branch, and updates the branch pointer.
|
||||
pub fn save<S: WriteRepository>(
|
||||
pub fn save<'r, S: WriteRepository<'r>>(
|
||||
&self,
|
||||
// TODO: This should be part of the signed refs.
|
||||
remote: &RemoteId,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use crate::decoder::Decoder;
|
|||
use crate::protocol::config::*;
|
||||
use crate::protocol::message::*;
|
||||
use crate::protocol::*;
|
||||
use crate::storage::{ReadStorage, WriteStorage};
|
||||
use crate::storage::WriteStorage;
|
||||
use crate::test::crypto::MockSigner;
|
||||
use crate::*;
|
||||
|
||||
|
|
@ -32,9 +32,9 @@ pub struct Peer<S> {
|
|||
initialized: bool,
|
||||
}
|
||||
|
||||
impl<S> simulator::Peer<Protocol<S>> for Peer<S>
|
||||
impl<'r, S> simulator::Peer<Protocol<S>> for Peer<S>
|
||||
where
|
||||
S: ReadStorage + WriteStorage + 'static,
|
||||
S: WriteStorage<'r> + 'static,
|
||||
{
|
||||
fn init(&mut self) {
|
||||
self.initialize()
|
||||
|
|
@ -59,9 +59,9 @@ impl<S> DerefMut for Peer<S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S> Peer<S>
|
||||
impl<'r, S> Peer<S>
|
||||
where
|
||||
S: ReadStorage + WriteStorage + 'static,
|
||||
S: WriteStorage<'r> + 'static,
|
||||
{
|
||||
pub fn new(name: &'static str, ip: impl Into<net::IpAddr>, storage: S) -> Self {
|
||||
Self::config(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::git;
|
|||
use crate::identity::{ProjId, Project, UserId};
|
||||
use crate::storage::refs;
|
||||
use crate::storage::{
|
||||
Error, Inventory, ReadRepository, ReadStorage, Remote, Remotes, WriteRepository, WriteStorage,
|
||||
Error, Inventory, ReadRepository, ReadStorage, Remote, RemoteId, WriteRepository, WriteStorage,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -56,7 +56,7 @@ impl ReadStorage for MockStorage {
|
|||
}
|
||||
}
|
||||
|
||||
impl WriteStorage for MockStorage {
|
||||
impl WriteStorage<'_> for MockStorage {
|
||||
type Repository = MockRepository;
|
||||
|
||||
fn repository(&self, _proj: &ProjId) -> Result<Self::Repository, Error> {
|
||||
|
|
@ -73,7 +73,13 @@ impl WriteStorage for MockStorage {
|
|||
|
||||
pub struct MockRepository {}
|
||||
|
||||
impl ReadRepository for MockRepository {
|
||||
impl ReadRepository<'_> for MockRepository {
|
||||
type Remotes = std::iter::Empty<Result<(RemoteId, Remote<Verified>), refs::Error>>;
|
||||
|
||||
fn is_empty(&self) -> Result<bool, git2::Error> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn path(&self) -> &std::path::Path {
|
||||
todo!()
|
||||
}
|
||||
|
|
@ -82,7 +88,7 @@ impl ReadRepository for MockRepository {
|
|||
todo!()
|
||||
}
|
||||
|
||||
fn remotes(&self) -> Result<Remotes<Verified>, refs::Error> {
|
||||
fn remotes(&self) -> Result<Self::Remotes, git2::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +121,7 @@ impl ReadRepository for MockRepository {
|
|||
}
|
||||
}
|
||||
|
||||
impl WriteRepository for MockRepository {
|
||||
impl WriteRepository<'_> for MockRepository {
|
||||
fn fetch(&mut self, _url: &Url) -> Result<(), git2::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue