1pub mod git;
2pub mod handle;
3pub mod policy;
4pub mod transport;
5
6pub(crate) mod sigrefs;
7
8mod refs;
9mod stage;
10mod state;
11
12use std::time::Instant;
13
14use gix_protocol::handshake;
15
16pub use gix_protocol::{transport::bstr::ByteSlice, RemoteProgress};
17pub use handle::Handle;
18pub use policy::{Allowed, BlockList, Scope};
19pub use state::{FetchLimit, FetchResult};
20pub use transport::Transport;
21
22use radicle::crypto::PublicKey;
23use radicle::storage::refs::RefsAt;
24use radicle::storage::ReadRepository as _;
25use state::FetchState;
26use thiserror::Error;
27
28#[derive(Debug, Error)]
29pub enum Error {
30 #[error("failed to perform fetch handshake: {0}")]
31 Handshake(#[from] Box<handshake::Error>),
32 #[error("failed to load `rad/id`")]
33 Identity {
34 #[source]
35 err: Box<dyn std::error::Error + Send + Sync + 'static>,
36 },
37 #[error(transparent)]
38 Protocol(#[from] state::error::Protocol),
39 #[error("missing `rad/id`")]
40 MissingRadId,
41 #[error("attempted to replicate from self")]
42 ReplicateSelf,
43}
44
45pub fn pull<S>(
51 handle: &mut Handle<S>,
52 limit: FetchLimit,
53 remote: PublicKey,
54 refs_at: Option<Vec<RefsAt>>,
55) -> Result<FetchResult, Error>
56where
57 S: transport::ConnectionStream,
58{
59 let start = Instant::now();
60 let local = *handle.local();
61 if local == remote {
62 return Err(Error::ReplicateSelf);
63 }
64 let handshake = perform_handshake(handle)?;
65 let state = FetchState::default();
66
67 handle.blocked.extend([local]);
69 let result = state
70 .run(handle, &handshake, limit, remote, refs_at)
71 .map_err(Error::Protocol);
72
73 log::debug!(
74 target: "fetch",
75 "Finished pull of {} ({}ms)",
76 handle.repo.id(),
77 start.elapsed().as_millis()
78 );
79 result
80}
81
82pub fn clone<S>(
87 handle: &mut Handle<S>,
88 limit: FetchLimit,
89 remote: PublicKey,
90) -> Result<FetchResult, Error>
91where
92 S: transport::ConnectionStream,
93{
94 let start = Instant::now();
95 if *handle.local() == remote {
96 return Err(Error::ReplicateSelf);
97 }
98 let handshake = perform_handshake(handle)?;
99 let state = FetchState::default();
100 let result = state
101 .run(handle, &handshake, limit, remote, None)
102 .map_err(Error::Protocol);
103 let elapsed = start.elapsed().as_millis();
104 let rid = handle.repo.id();
105
106 match &result {
107 Ok(_) => {
108 log::debug!(
109 target: "fetch",
110 "Finished clone of {rid} from {remote} ({elapsed}ms)",
111 );
112 }
113 Err(e) => {
114 log::debug!(
115 target: "fetch",
116 "Clone of {rid} from {remote} failed with '{e}' ({elapsed}ms)",
117 );
118 }
119 }
120 result
121}
122
123fn perform_handshake<S>(handle: &mut Handle<S>) -> Result<handshake::Outcome, Error>
124where
125 S: transport::ConnectionStream,
126{
127 let result = handle.transport.handshake();
128
129 if let Err(err) = &result {
130 log::warn!(target: "fetch", "Failed to perform handshake: {err}");
131 }
132
133 Ok(result?)
134}