node: Turn remotes into an iterator

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier 2022-09-04 13:15:26 +02:00
parent 078184b7d6
commit 7be6fe3414
No known key found for this signature in database
8 changed files with 80 additions and 52 deletions

View File

@ -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(

View File

@ -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() {

View File

@ -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,

View File

@ -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;

View File

@ -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();

View File

@ -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,

View File

@ -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(

View File

@ -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(())
}