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};
|
||||
|
||||
/// 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,
|
||||
repo: Repository,
|
||||
repo: R,
|
||||
pub(crate) allowed: Allowed,
|
||||
pub(crate) transport: Transport<S>,
|
||||
/// The set of keys we will ignore when fetching from a
|
||||
|
|
@ -29,39 +29,11 @@ pub struct Handle<S> {
|
|||
pub(crate) interrupt: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl<S> Handle<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)),
|
||||
})
|
||||
}
|
||||
|
||||
impl<R, S> Handle<R, S> {
|
||||
pub fn is_blocked(&self, key: &PublicKey) -> bool {
|
||||
self.blocked.is_blocked(key)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn repository(&self) -> &Repository {
|
||||
&self.repo
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn local(&self) -> &PublicKey {
|
||||
&self.local
|
||||
|
|
@ -71,15 +43,52 @@ impl<S> Handle<S> {
|
|||
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 {
|
||||
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 {
|
||||
use radicle::node::policy;
|
||||
use radicle::prelude::RepoId;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use gix_protocol::handshake;
|
|||
pub use gix_protocol::{transport::bstr::ByteSlice, RemoteProgress};
|
||||
pub use handle::Handle;
|
||||
pub use policy::{Allowed, BlockList, Scope};
|
||||
use radicle::storage::git::Repository;
|
||||
pub use state::{FetchLimit, FetchResult};
|
||||
pub use transport::Transport;
|
||||
|
||||
|
|
@ -47,13 +48,14 @@ pub enum Error {
|
|||
/// 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
|
||||
/// [`clone`] should be used.
|
||||
pub fn pull<S>(
|
||||
handle: &mut Handle<S>,
|
||||
pub fn pull<R, S>(
|
||||
handle: &mut Handle<R, S>,
|
||||
limit: FetchLimit,
|
||||
remote: PublicKey,
|
||||
refs_at: Option<Vec<RefsAt>>,
|
||||
) -> Result<FetchResult, Error>
|
||||
where
|
||||
R: AsRef<Repository>,
|
||||
S: transport::ConnectionStream,
|
||||
{
|
||||
let start = Instant::now();
|
||||
|
|
@ -83,12 +85,13 @@ where
|
|||
///
|
||||
/// It is expected that the local peer has an empty repository which
|
||||
/// they want to populate with the `remote`'s view of the project.
|
||||
pub fn clone<S>(
|
||||
handle: &mut Handle<S>,
|
||||
pub fn clone<R, S>(
|
||||
handle: &mut Handle<R, S>,
|
||||
limit: FetchLimit,
|
||||
remote: PublicKey,
|
||||
) -> Result<FetchResult, Error>
|
||||
where
|
||||
R: AsRef<Repository>,
|
||||
S: transport::ConnectionStream,
|
||||
{
|
||||
let start = Instant::now();
|
||||
|
|
@ -120,7 +123,7 @@ where
|
|||
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
|
||||
S: transport::ConnectionStream,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::ops::{Deref, Not as _};
|
||||
|
||||
use radicle::storage::git::Repository;
|
||||
pub use radicle::storage::refs::SignedRefsAt;
|
||||
pub use radicle::storage::{git::Validation, Validations};
|
||||
use radicle::{crypto::PublicKey, storage::ValidateRepository};
|
||||
|
|
@ -53,10 +54,13 @@ impl<T> DelegateStatus<T> {
|
|||
|
||||
/// Construct a `DelegateStatus` with [`SignedRefsAt`] signed reference
|
||||
/// data, if it can be found in `repo`.
|
||||
pub fn load<S>(
|
||||
pub fn load<R, S>(
|
||||
self,
|
||||
cached: &Cached<S>,
|
||||
) -> Result<DelegateStatus<Option<SignedRefsAt>>, radicle::storage::refs::Error> {
|
||||
cached: &Cached<R, S>,
|
||||
) -> Result<DelegateStatus<Option<SignedRefsAt>>, radicle::storage::refs::Error>
|
||||
where
|
||||
R: AsRef<Repository>,
|
||||
{
|
||||
let remote = *self.remote();
|
||||
self.traverse(|_| cached.load(&remote))
|
||||
}
|
||||
|
|
@ -102,10 +106,13 @@ impl RemoteRefs {
|
|||
///
|
||||
/// If the sigrefs are missing for a given remote, regardless of delegate
|
||||
/// status, then that remote is filtered out.
|
||||
pub(crate) fn load<'a, S>(
|
||||
cached: &Cached<S>,
|
||||
pub(crate) fn load<'a, R, S>(
|
||||
cached: &Cached<R, S>,
|
||||
remotes: impl Iterator<Item = &'a PublicKey>,
|
||||
) -> Result<Self, error::RemoteRefs> {
|
||||
) -> Result<Self, error::RemoteRefs>
|
||||
where
|
||||
R: AsRef<Repository>,
|
||||
{
|
||||
remotes
|
||||
.filter_map(|id| match cached.load(id) {
|
||||
Ok(None) => None,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use radicle::identity::{Did, Doc, DocError};
|
|||
|
||||
use radicle::prelude::Verified;
|
||||
use radicle::storage;
|
||||
use radicle::storage::git::Repository;
|
||||
use radicle::storage::refs::RefsAt;
|
||||
use radicle::storage::{
|
||||
git::Validation, Remote, RemoteId, RemoteRepository, Remotes, ValidateRepository, Validations,
|
||||
|
|
@ -196,7 +197,10 @@ impl FetchState {
|
|||
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 {
|
||||
handle,
|
||||
state: self,
|
||||
|
|
@ -207,13 +211,14 @@ impl FetchState {
|
|||
impl FetchState {
|
||||
/// Perform the ls-refs and fetch for the given `step`. The result
|
||||
/// 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,
|
||||
handle: &mut Handle<S>,
|
||||
handle: &mut Handle<R, S>,
|
||||
handshake: &handshake::Outcome,
|
||||
step: &F,
|
||||
) -> Result<BTreeSet<PublicKey>, error::Step>
|
||||
where
|
||||
R: AsRef<Repository>,
|
||||
S: transport::ConnectionStream,
|
||||
F: ProtocolStage,
|
||||
{
|
||||
|
|
@ -280,9 +285,9 @@ impl FetchState {
|
|||
/// The resulting [`sigrefs::RemoteRefs`] will be the set of
|
||||
/// `rad/sigrefs` of the fetched remotes.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn run_special_refs<S>(
|
||||
fn run_special_refs<R, S>(
|
||||
&mut self,
|
||||
handle: &mut Handle<S>,
|
||||
handle: &mut Handle<R, S>,
|
||||
handshake: &handshake::Outcome,
|
||||
delegates: BTreeSet<PublicKey>,
|
||||
threshold: usize,
|
||||
|
|
@ -291,6 +296,7 @@ impl FetchState {
|
|||
refs_at: Option<Vec<RefsAt>>,
|
||||
) -> Result<sigrefs::RemoteRefs, error::Protocol>
|
||||
where
|
||||
R: AsRef<Repository>,
|
||||
S: transport::ConnectionStream,
|
||||
{
|
||||
match refs_at {
|
||||
|
|
@ -348,15 +354,16 @@ impl FetchState {
|
|||
/// of updating tips.
|
||||
/// 7. Apply the valid tips, iff no delegates failed validation.
|
||||
/// 8. Signal to the other side that the process has completed.
|
||||
pub(super) fn run<S>(
|
||||
pub(super) fn run<R, S>(
|
||||
mut self,
|
||||
handle: &mut Handle<S>,
|
||||
handle: &mut Handle<R, S>,
|
||||
handshake: &handshake::Outcome,
|
||||
limit: FetchLimit,
|
||||
remote: PublicKey,
|
||||
refs_at: Option<Vec<RefsAt>>,
|
||||
) -> Result<FetchResult, error::Protocol>
|
||||
where
|
||||
R: AsRef<Repository>,
|
||||
S: transport::ConnectionStream,
|
||||
{
|
||||
let start = Instant::now();
|
||||
|
|
@ -598,12 +605,15 @@ impl FetchState {
|
|||
|
||||
/// A cached version of [`Handle`] by using the underlying
|
||||
/// [`FetchState`]'s data for performing lookups.
|
||||
pub(crate) struct Cached<'a, S> {
|
||||
handle: &'a mut Handle<S>,
|
||||
pub(crate) struct Cached<'a, R, S> {
|
||||
handle: &'a mut Handle<R, S>,
|
||||
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
|
||||
/// [`FetchState`] and falling back to the [`Handle::refdb`].
|
||||
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> {
|
||||
// N.b. this is unused so we just delegate to the underlying
|
||||
// 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
|
||||
// not have a reference to the COB if they have not interacted
|
||||
// with it.
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ use super::channels::ChannelsFlush;
|
|||
|
||||
pub enum Handle {
|
||||
Clone {
|
||||
handle: radicle_fetch::Handle<ChannelsFlush>,
|
||||
handle: radicle_fetch::Handle<Repository, ChannelsFlush>,
|
||||
tmp: tempfile::TempDir,
|
||||
},
|
||||
Pull {
|
||||
handle: radicle_fetch::Handle<ChannelsFlush>,
|
||||
handle: radicle_fetch::Handle<Repository, ChannelsFlush>,
|
||||
notifications: node::notifications::StoreWriter,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -285,6 +285,12 @@ pub struct Repository {
|
|||
pub backend: git2::Repository,
|
||||
}
|
||||
|
||||
impl AsRef<Repository> for Repository {
|
||||
fn as_ref(&self) -> &Repository {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl git::canonical::effects::Ancestry for Repository {
|
||||
fn graph_ahead_behind(
|
||||
&self,
|
||||
|
|
|
|||
Loading…
Reference in New Issue