fetch: use `AsRef<Repository>`
Allow the fetch interface to accept anything that implements `AsRef<Repository>`. This allows flexibility in the types that the `Handle` can accept. This change is motivated by wanting to introduce a type that is a temporary repository that wraps a `Repository`.
This commit is contained in:
parent
a163f4e939
commit
e40fe86ff8
|
|
@ -12,9 +12,9 @@ use crate::policy::{Allowed, BlockList};
|
||||||
use crate::transport::{ConnectionStream, Transport};
|
use crate::transport::{ConnectionStream, Transport};
|
||||||
|
|
||||||
/// The handle used for pulling or cloning changes from a remote peer.
|
/// The handle used for pulling or cloning changes from a remote peer.
|
||||||
pub struct Handle<S> {
|
pub struct Handle<R, S> {
|
||||||
pub(crate) local: PublicKey,
|
pub(crate) local: PublicKey,
|
||||||
repo: Repository,
|
repo: R,
|
||||||
pub(crate) allowed: Allowed,
|
pub(crate) allowed: Allowed,
|
||||||
pub(crate) transport: Transport<S>,
|
pub(crate) transport: Transport<S>,
|
||||||
/// The set of keys we will ignore when fetching from a
|
/// The set of keys we will ignore when fetching from a
|
||||||
|
|
@ -29,39 +29,11 @@ pub struct Handle<S> {
|
||||||
pub(crate) interrupt: Arc<AtomicBool>,
|
pub(crate) interrupt: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Handle<S> {
|
impl<R, S> Handle<R, S> {
|
||||||
pub fn new(
|
|
||||||
local: PublicKey,
|
|
||||||
repo: Repository,
|
|
||||||
follow: Allowed,
|
|
||||||
blocked: BlockList,
|
|
||||||
connection: S,
|
|
||||||
) -> Result<Self, error::Init>
|
|
||||||
where
|
|
||||||
S: ConnectionStream,
|
|
||||||
{
|
|
||||||
let git_dir = repo.backend.path().to_path_buf();
|
|
||||||
let transport = Transport::new(git_dir, BString::from(repo.id.canonical()), connection);
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
local,
|
|
||||||
repo,
|
|
||||||
allowed: follow,
|
|
||||||
transport,
|
|
||||||
blocked,
|
|
||||||
interrupt: Arc::new(AtomicBool::new(false)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_blocked(&self, key: &PublicKey) -> bool {
|
pub fn is_blocked(&self, key: &PublicKey) -> bool {
|
||||||
self.blocked.is_blocked(key)
|
self.blocked.is_blocked(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn repository(&self) -> &Repository {
|
|
||||||
&self.repo
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn local(&self) -> &PublicKey {
|
pub fn local(&self) -> &PublicKey {
|
||||||
&self.local
|
&self.local
|
||||||
|
|
@ -71,15 +43,52 @@ impl<S> Handle<S> {
|
||||||
self.interrupt.store(true, atomic::Ordering::Relaxed);
|
self.interrupt.store(true, atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verified(&self, head: Oid) -> Result<Doc, DocError> {
|
|
||||||
Ok(self.repo.identity_doc_at(head)?.doc)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn allowed(&self) -> Allowed {
|
pub fn allowed(&self) -> Allowed {
|
||||||
self.allowed.clone()
|
self.allowed.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<R, S> Handle<R, S>
|
||||||
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
local: PublicKey,
|
||||||
|
repo: R,
|
||||||
|
follow: Allowed,
|
||||||
|
blocked: BlockList,
|
||||||
|
connection: S,
|
||||||
|
) -> Result<Self, error::Init>
|
||||||
|
where
|
||||||
|
S: ConnectionStream,
|
||||||
|
{
|
||||||
|
let git_dir = repo.as_ref().backend.path().to_path_buf();
|
||||||
|
let transport = Transport::new(
|
||||||
|
git_dir,
|
||||||
|
BString::from(repo.as_ref().id.canonical()),
|
||||||
|
connection,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
local,
|
||||||
|
repo,
|
||||||
|
allowed: follow,
|
||||||
|
transport,
|
||||||
|
blocked,
|
||||||
|
interrupt: Arc::new(AtomicBool::new(false)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn repository(&self) -> &Repository {
|
||||||
|
self.repo.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verified(&self, head: Oid) -> Result<Doc, DocError> {
|
||||||
|
Ok(self.repository().identity_doc_at(head)?.doc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod error {
|
pub mod error {
|
||||||
use radicle::node::policy;
|
use radicle::node::policy;
|
||||||
use radicle::prelude::RepoId;
|
use radicle::prelude::RepoId;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ use gix_protocol::handshake;
|
||||||
pub use gix_protocol::{transport::bstr::ByteSlice, RemoteProgress};
|
pub use gix_protocol::{transport::bstr::ByteSlice, RemoteProgress};
|
||||||
pub use handle::Handle;
|
pub use handle::Handle;
|
||||||
pub use policy::{Allowed, BlockList, Scope};
|
pub use policy::{Allowed, BlockList, Scope};
|
||||||
|
use radicle::storage::git::Repository;
|
||||||
pub use state::{FetchLimit, FetchResult};
|
pub use state::{FetchLimit, FetchResult};
|
||||||
pub use transport::Transport;
|
pub use transport::Transport;
|
||||||
|
|
||||||
|
|
@ -47,13 +48,14 @@ pub enum Error {
|
||||||
/// It is expected that the local peer has a copy of the repository
|
/// It is expected that the local peer has a copy of the repository
|
||||||
/// and is pulling new changes. If the repository does not exist, then
|
/// and is pulling new changes. If the repository does not exist, then
|
||||||
/// [`clone`] should be used.
|
/// [`clone`] should be used.
|
||||||
pub fn pull<S>(
|
pub fn pull<R, S>(
|
||||||
handle: &mut Handle<S>,
|
handle: &mut Handle<R, S>,
|
||||||
limit: FetchLimit,
|
limit: FetchLimit,
|
||||||
remote: PublicKey,
|
remote: PublicKey,
|
||||||
refs_at: Option<Vec<RefsAt>>,
|
refs_at: Option<Vec<RefsAt>>,
|
||||||
) -> Result<FetchResult, Error>
|
) -> Result<FetchResult, Error>
|
||||||
where
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
S: transport::ConnectionStream,
|
S: transport::ConnectionStream,
|
||||||
{
|
{
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
@ -83,12 +85,13 @@ where
|
||||||
///
|
///
|
||||||
/// It is expected that the local peer has an empty repository which
|
/// It is expected that the local peer has an empty repository which
|
||||||
/// they want to populate with the `remote`'s view of the project.
|
/// they want to populate with the `remote`'s view of the project.
|
||||||
pub fn clone<S>(
|
pub fn clone<R, S>(
|
||||||
handle: &mut Handle<S>,
|
handle: &mut Handle<R, S>,
|
||||||
limit: FetchLimit,
|
limit: FetchLimit,
|
||||||
remote: PublicKey,
|
remote: PublicKey,
|
||||||
) -> Result<FetchResult, Error>
|
) -> Result<FetchResult, Error>
|
||||||
where
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
S: transport::ConnectionStream,
|
S: transport::ConnectionStream,
|
||||||
{
|
{
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
@ -120,7 +123,7 @@ where
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform_handshake<S>(handle: &mut Handle<S>) -> Result<handshake::Outcome, Error>
|
fn perform_handshake<R, S>(handle: &mut Handle<R, S>) -> Result<handshake::Outcome, Error>
|
||||||
where
|
where
|
||||||
S: transport::ConnectionStream,
|
S: transport::ConnectionStream,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::ops::{Deref, Not as _};
|
use std::ops::{Deref, Not as _};
|
||||||
|
|
||||||
|
use radicle::storage::git::Repository;
|
||||||
pub use radicle::storage::refs::SignedRefsAt;
|
pub use radicle::storage::refs::SignedRefsAt;
|
||||||
pub use radicle::storage::{git::Validation, Validations};
|
pub use radicle::storage::{git::Validation, Validations};
|
||||||
use radicle::{crypto::PublicKey, storage::ValidateRepository};
|
use radicle::{crypto::PublicKey, storage::ValidateRepository};
|
||||||
|
|
@ -53,10 +54,13 @@ impl<T> DelegateStatus<T> {
|
||||||
|
|
||||||
/// Construct a `DelegateStatus` with [`SignedRefsAt`] signed reference
|
/// Construct a `DelegateStatus` with [`SignedRefsAt`] signed reference
|
||||||
/// data, if it can be found in `repo`.
|
/// data, if it can be found in `repo`.
|
||||||
pub fn load<S>(
|
pub fn load<R, S>(
|
||||||
self,
|
self,
|
||||||
cached: &Cached<S>,
|
cached: &Cached<R, S>,
|
||||||
) -> Result<DelegateStatus<Option<SignedRefsAt>>, radicle::storage::refs::Error> {
|
) -> Result<DelegateStatus<Option<SignedRefsAt>>, radicle::storage::refs::Error>
|
||||||
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
|
{
|
||||||
let remote = *self.remote();
|
let remote = *self.remote();
|
||||||
self.traverse(|_| cached.load(&remote))
|
self.traverse(|_| cached.load(&remote))
|
||||||
}
|
}
|
||||||
|
|
@ -102,10 +106,13 @@ impl RemoteRefs {
|
||||||
///
|
///
|
||||||
/// If the sigrefs are missing for a given remote, regardless of delegate
|
/// If the sigrefs are missing for a given remote, regardless of delegate
|
||||||
/// status, then that remote is filtered out.
|
/// status, then that remote is filtered out.
|
||||||
pub(crate) fn load<'a, S>(
|
pub(crate) fn load<'a, R, S>(
|
||||||
cached: &Cached<S>,
|
cached: &Cached<R, S>,
|
||||||
remotes: impl Iterator<Item = &'a PublicKey>,
|
remotes: impl Iterator<Item = &'a PublicKey>,
|
||||||
) -> Result<Self, error::RemoteRefs> {
|
) -> Result<Self, error::RemoteRefs>
|
||||||
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
|
{
|
||||||
remotes
|
remotes
|
||||||
.filter_map(|id| match cached.load(id) {
|
.filter_map(|id| match cached.load(id) {
|
||||||
Ok(None) => None,
|
Ok(None) => None,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use radicle::identity::{Did, Doc, DocError};
|
||||||
|
|
||||||
use radicle::prelude::Verified;
|
use radicle::prelude::Verified;
|
||||||
use radicle::storage;
|
use radicle::storage;
|
||||||
|
use radicle::storage::git::Repository;
|
||||||
use radicle::storage::refs::RefsAt;
|
use radicle::storage::refs::RefsAt;
|
||||||
use radicle::storage::{
|
use radicle::storage::{
|
||||||
git::Validation, Remote, RemoteId, RemoteRepository, Remotes, ValidateRepository, Validations,
|
git::Validation, Remote, RemoteId, RemoteRepository, Remotes, ValidateRepository, Validations,
|
||||||
|
|
@ -196,7 +197,10 @@ impl FetchState {
|
||||||
ap
|
ap
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn as_cached<'a, S>(&'a mut self, handle: &'a mut Handle<S>) -> Cached<'a, S> {
|
pub(crate) fn as_cached<'a, R, S>(
|
||||||
|
&'a mut self,
|
||||||
|
handle: &'a mut Handle<R, S>,
|
||||||
|
) -> Cached<'a, R, S> {
|
||||||
Cached {
|
Cached {
|
||||||
handle,
|
handle,
|
||||||
state: self,
|
state: self,
|
||||||
|
|
@ -207,13 +211,14 @@ impl FetchState {
|
||||||
impl FetchState {
|
impl FetchState {
|
||||||
/// Perform the ls-refs and fetch for the given `step`. The result
|
/// Perform the ls-refs and fetch for the given `step`. The result
|
||||||
/// of these processes is kept track of in the internal state.
|
/// of these processes is kept track of in the internal state.
|
||||||
pub(super) fn run_stage<S, F>(
|
pub(super) fn run_stage<R, S, F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &mut Handle<S>,
|
handle: &mut Handle<R, S>,
|
||||||
handshake: &handshake::Outcome,
|
handshake: &handshake::Outcome,
|
||||||
step: &F,
|
step: &F,
|
||||||
) -> Result<BTreeSet<PublicKey>, error::Step>
|
) -> Result<BTreeSet<PublicKey>, error::Step>
|
||||||
where
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
S: transport::ConnectionStream,
|
S: transport::ConnectionStream,
|
||||||
F: ProtocolStage,
|
F: ProtocolStage,
|
||||||
{
|
{
|
||||||
|
|
@ -280,9 +285,9 @@ impl FetchState {
|
||||||
/// The resulting [`sigrefs::RemoteRefs`] will be the set of
|
/// The resulting [`sigrefs::RemoteRefs`] will be the set of
|
||||||
/// `rad/sigrefs` of the fetched remotes.
|
/// `rad/sigrefs` of the fetched remotes.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn run_special_refs<S>(
|
fn run_special_refs<R, S>(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &mut Handle<S>,
|
handle: &mut Handle<R, S>,
|
||||||
handshake: &handshake::Outcome,
|
handshake: &handshake::Outcome,
|
||||||
delegates: BTreeSet<PublicKey>,
|
delegates: BTreeSet<PublicKey>,
|
||||||
threshold: usize,
|
threshold: usize,
|
||||||
|
|
@ -291,6 +296,7 @@ impl FetchState {
|
||||||
refs_at: Option<Vec<RefsAt>>,
|
refs_at: Option<Vec<RefsAt>>,
|
||||||
) -> Result<sigrefs::RemoteRefs, error::Protocol>
|
) -> Result<sigrefs::RemoteRefs, error::Protocol>
|
||||||
where
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
S: transport::ConnectionStream,
|
S: transport::ConnectionStream,
|
||||||
{
|
{
|
||||||
match refs_at {
|
match refs_at {
|
||||||
|
|
@ -348,15 +354,16 @@ impl FetchState {
|
||||||
/// of updating tips.
|
/// of updating tips.
|
||||||
/// 7. Apply the valid tips, iff no delegates failed validation.
|
/// 7. Apply the valid tips, iff no delegates failed validation.
|
||||||
/// 8. Signal to the other side that the process has completed.
|
/// 8. Signal to the other side that the process has completed.
|
||||||
pub(super) fn run<S>(
|
pub(super) fn run<R, S>(
|
||||||
mut self,
|
mut self,
|
||||||
handle: &mut Handle<S>,
|
handle: &mut Handle<R, S>,
|
||||||
handshake: &handshake::Outcome,
|
handshake: &handshake::Outcome,
|
||||||
limit: FetchLimit,
|
limit: FetchLimit,
|
||||||
remote: PublicKey,
|
remote: PublicKey,
|
||||||
refs_at: Option<Vec<RefsAt>>,
|
refs_at: Option<Vec<RefsAt>>,
|
||||||
) -> Result<FetchResult, error::Protocol>
|
) -> Result<FetchResult, error::Protocol>
|
||||||
where
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
S: transport::ConnectionStream,
|
S: transport::ConnectionStream,
|
||||||
{
|
{
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
@ -598,12 +605,15 @@ impl FetchState {
|
||||||
|
|
||||||
/// A cached version of [`Handle`] by using the underlying
|
/// A cached version of [`Handle`] by using the underlying
|
||||||
/// [`FetchState`]'s data for performing lookups.
|
/// [`FetchState`]'s data for performing lookups.
|
||||||
pub(crate) struct Cached<'a, S> {
|
pub(crate) struct Cached<'a, R, S> {
|
||||||
handle: &'a mut Handle<S>,
|
handle: &'a mut Handle<R, S>,
|
||||||
state: &'a mut FetchState,
|
state: &'a mut FetchState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Cached<'_, S> {
|
impl<R, S> Cached<'_, R, S>
|
||||||
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
|
{
|
||||||
/// Resolves `refname` to its [`ObjectId`] by first looking at the
|
/// Resolves `refname` to its [`ObjectId`] by first looking at the
|
||||||
/// [`FetchState`] and falling back to the [`Handle::refdb`].
|
/// [`FetchState`] and falling back to the [`Handle::refdb`].
|
||||||
pub fn refname_to_id<'b, N>(
|
pub fn refname_to_id<'b, N>(
|
||||||
|
|
@ -651,7 +661,10 @@ impl<S> Cached<'_, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> RemoteRepository for Cached<'_, S> {
|
impl<R, S> RemoteRepository for Cached<'_, R, S>
|
||||||
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
|
{
|
||||||
fn remote(&self, remote: &RemoteId) -> Result<Remote, storage::refs::Error> {
|
fn remote(&self, remote: &RemoteId) -> Result<Remote, storage::refs::Error> {
|
||||||
// N.b. this is unused so we just delegate to the underlying
|
// N.b. this is unused so we just delegate to the underlying
|
||||||
// repository for a correct implementation.
|
// repository for a correct implementation.
|
||||||
|
|
@ -671,7 +684,10 @@ impl<S> RemoteRepository for Cached<'_, S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> ValidateRepository for Cached<'_, S> {
|
impl<R, S> ValidateRepository for Cached<'_, R, S>
|
||||||
|
where
|
||||||
|
R: AsRef<Repository>,
|
||||||
|
{
|
||||||
// N.b. we don't verify the `rad/id` of each remote since they may
|
// N.b. we don't verify the `rad/id` of each remote since they may
|
||||||
// not have a reference to the COB if they have not interacted
|
// not have a reference to the COB if they have not interacted
|
||||||
// with it.
|
// with it.
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ use super::channels::ChannelsFlush;
|
||||||
|
|
||||||
pub enum Handle {
|
pub enum Handle {
|
||||||
Clone {
|
Clone {
|
||||||
handle: radicle_fetch::Handle<ChannelsFlush>,
|
handle: radicle_fetch::Handle<Repository, ChannelsFlush>,
|
||||||
tmp: tempfile::TempDir,
|
tmp: tempfile::TempDir,
|
||||||
},
|
},
|
||||||
Pull {
|
Pull {
|
||||||
handle: radicle_fetch::Handle<ChannelsFlush>,
|
handle: radicle_fetch::Handle<Repository, ChannelsFlush>,
|
||||||
notifications: node::notifications::StoreWriter,
|
notifications: node::notifications::StoreWriter,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,12 @@ pub struct Repository {
|
||||||
pub backend: git2::Repository,
|
pub backend: git2::Repository,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<Repository> for Repository {
|
||||||
|
fn as_ref(&self) -> &Repository {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl git::canonical::effects::Ancestry for Repository {
|
impl git::canonical::effects::Ancestry for Repository {
|
||||||
fn graph_ahead_behind(
|
fn graph_ahead_behind(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue