radicle_fetch/
handle.rs

1use std::sync::atomic::{self, AtomicBool};
2use std::sync::Arc;
3
4use bstr::BString;
5use radicle::crypto::PublicKey;
6use radicle::git::Oid;
7use radicle::identity::{Doc, DocError};
8use radicle::storage::git::Repository;
9use radicle::storage::ReadRepository;
10
11use crate::policy::{Allowed, BlockList};
12use crate::transport::{ConnectionStream, Transport};
13
14/// The handle used for pulling or cloning changes from a remote peer.
15pub struct Handle<R, S> {
16    pub(crate) local: PublicKey,
17    repo: R,
18    pub(crate) allowed: Allowed,
19    pub(crate) transport: Transport<S>,
20    /// The set of keys we will ignore when fetching from a
21    /// remote. This set can be constructed using the tracking
22    /// `config`'s blocked node entries.
23    ///
24    /// Note that it's important to ignore the local peer's
25    /// key in [`crate::pull`], however, we choose to allow the local
26    /// peer's key in [`crate::clone`].
27    pub(crate) blocked: BlockList,
28    // Signals to the pack writer to interrupt the process
29    pub(crate) interrupt: Arc<AtomicBool>,
30}
31
32impl<R, S> Handle<R, S> {
33    pub fn is_blocked(&self, key: &PublicKey) -> bool {
34        self.blocked.is_blocked(key)
35    }
36
37    #[inline]
38    pub fn local(&self) -> &PublicKey {
39        &self.local
40    }
41
42    pub fn interrupt_pack_writer(&mut self) {
43        self.interrupt.store(true, atomic::Ordering::Relaxed);
44    }
45
46    pub fn allowed(&self) -> Allowed {
47        self.allowed.clone()
48    }
49
50    pub fn into_inner(self) -> R {
51        self.repo
52    }
53}
54
55impl<R, S> Handle<R, S>
56where
57    R: AsRef<Repository>,
58{
59    pub fn new(
60        local: PublicKey,
61        repo: R,
62        follow: Allowed,
63        blocked: BlockList,
64        connection: S,
65    ) -> Result<Self, error::Init>
66    where
67        S: ConnectionStream,
68    {
69        let git_dir = repo.as_ref().backend.path().to_path_buf();
70        let transport = Transport::new(
71            git_dir,
72            BString::from(repo.as_ref().id.canonical()),
73            connection,
74        );
75
76        Ok(Self {
77            local,
78            repo,
79            allowed: follow,
80            transport,
81            blocked,
82            interrupt: Arc::new(AtomicBool::new(false)),
83        })
84    }
85
86    #[inline]
87    pub fn repository(&self) -> &Repository {
88        self.repo.as_ref()
89    }
90
91    pub fn verified(&self, head: Oid) -> Result<Doc, DocError> {
92        Ok(self.repository().identity_doc_at(head)?.doc)
93    }
94}
95
96pub mod error {
97    use radicle::node::policy;
98    use radicle::prelude::RepoId;
99    use radicle::{git, storage};
100    use thiserror::Error;
101
102    #[derive(Debug, Error)]
103    pub enum Init {
104        #[error(transparent)]
105        Tracking(#[from] policy::config::Error),
106    }
107
108    #[derive(Debug, Error)]
109    pub enum Tracking {
110        #[error("failed to find policy for {rid}")]
111        FailedPolicy {
112            rid: RepoId,
113            #[source]
114            err: policy::store::Error,
115        },
116        #[error("cannot fetch {rid} as it is not seeded")]
117        BlockedPolicy { rid: RepoId },
118        #[error("failed to get tracking nodes for {rid}")]
119        FailedNodes {
120            rid: RepoId,
121            #[source]
122            err: policy::store::Error,
123        },
124
125        #[error(transparent)]
126        Storage(#[from] storage::Error),
127
128        #[error(transparent)]
129        Git(#[from] git::raw::Error),
130
131        #[error(transparent)]
132        Refs(#[from] storage::refs::Error),
133    }
134}