Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
node: Turn remotes into an iterator
Alexis Sellier committed 3 years ago
commit 7be6fe341459b1a0d666e6086ae83d083173bba8
parent 078184b7d65a1c46d2f75a3a807b8b12e98ccca0
8 files changed +80 -52
modified node/src/protocol.rs
@@ -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(
modified node/src/protocol/peer.rs
@@ -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() {
modified node/src/rad.rs
@@ -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,
modified node/src/storage.rs
@@ -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;

modified node/src/storage/git.rs
@@ -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();

modified node/src/storage/refs.rs
@@ -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,
modified node/src/test/peer.rs
@@ -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(
modified node/src/test/storage.rs
@@ -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(())
    }