1use bstr::BString;
2
3pub mod refs;
5
6#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum Ref {
10 Peeled {
12 full_ref_name: BString,
14 tag: gix_hash::ObjectId,
16 object: gix_hash::ObjectId,
18 },
19 Direct {
21 full_ref_name: BString,
23 object: gix_hash::ObjectId,
25 },
26 Symbolic {
28 full_ref_name: BString,
30 target: BString,
36 tag: Option<gix_hash::ObjectId>,
40 object: gix_hash::ObjectId,
42 },
43 Unborn {
46 full_ref_name: BString,
48 target: BString,
50 },
51}
52
53#[cfg(feature = "handshake")]
54pub(crate) mod hero {
55 use crate::handshake::Ref;
56
57 #[derive(Default, Debug, Clone)]
59 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60 pub struct Handshake {
61 pub server_protocol_version: gix_transport::Protocol,
63 pub refs: Option<Vec<Ref>>,
65 pub v1_shallow_updates: Option<Vec<crate::fetch::response::ShallowUpdate>>,
68 pub capabilities: gix_transport::client::Capabilities,
70 }
71
72 #[cfg(feature = "fetch")]
73 mod fetch {
74 use crate::{Handshake, command::Feature, fetch::RefMap, ls_refs::RefPrefixes};
75 use gix_features::progress::Progress;
76
77 pub enum ObtainRefMap<'a> {
79 Existing(RefMap),
81 LsRefsCommand(crate::LsRefsCommand<'a>, crate::fetch::refmap::init::Context),
83 }
84
85 macro_rules! fetch {
86 ($name:ident, $bisync:path, $transport:path, $invoke:ident, $mode:literal) => {
87 #[$bisync]
89 pub async fn $name(
90 self,
91 mut progress: impl Progress,
92 transport: &mut impl $transport,
93 trace_packetlines: bool,
94 ) -> Result<RefMap, crate::fetch::refmap::init::Error> {
95 let (cmd, cx) = match self {
96 ObtainRefMap::Existing(map) => return Ok(map),
97 ObtainRefMap::LsRefsCommand(cmd, cx) => (cmd, cx),
98 };
99
100 let _span = gix_trace::coarse!(
101 "gix_protocol::handshake::ObtainRefMap::fetch()",
102 mode = $mode
103 );
104 let capabilities = cmd.capabilities;
105 let remote_refs = cmd
106 .$invoke(transport, &mut progress, trace_packetlines)
107 .await?;
108 RefMap::from_refs(remote_refs, capabilities, cx)
109 }
110 };
111 }
112
113 impl ObtainRefMap<'_> {
114 #[cfg(feature = "async-client")]
115 fetch!(
116 fetch_async,
117 ::bisync::asynchronous::bisync,
118 crate::transport::client::async_io::Transport,
119 invoke_async,
120 "async"
121 );
122
123 #[cfg(feature = "blocking-client")]
124 fetch!(
125 fetch_blocking,
126 ::bisync::synchronous::bisync,
127 crate::transport::client::blocking_io::Transport,
128 invoke_blocking,
129 "blocking"
130 );
131 }
132
133 impl Handshake {
134 pub fn prepare_lsrefs_or_extract_refmap(
136 &mut self,
137 user_agent: Feature,
138 prefix_from_spec_as_filter_on_remote: bool,
139 refmap_context: crate::fetch::refmap::init::Context,
140 ) -> Result<ObtainRefMap<'_>, crate::fetch::refmap::init::Error> {
141 if let Some(refs) = self.refs.take() {
142 return Ok(ObtainRefMap::Existing(RefMap::from_refs(
143 refs,
144 &self.capabilities,
145 refmap_context,
146 )?));
147 }
148
149 let prefix_refs = prefix_from_spec_as_filter_on_remote.then(|| {
150 let all_refspecs = refmap_context.aggregate_refspecs();
151 RefPrefixes::from_refspecs(&all_refspecs)
152 });
153 Ok(ObtainRefMap::LsRefsCommand(
154 crate::LsRefsCommand::new(prefix_refs, &self.capabilities, user_agent),
155 refmap_context,
156 ))
157 }
158 }
159 }
160}
161
162#[cfg(feature = "handshake")]
163mod error {
164 use bstr::BString;
165 use gix_transport::client;
166
167 use crate::{credentials, handshake::refs};
168
169 #[derive(Debug, thiserror::Error)]
171 #[expect(missing_docs)]
172 pub enum Error {
173 #[error("Failed to obtain credentials")]
174 Credentials(#[from] credentials::protocol::Error),
175 #[error("No credentials were returned at all as if the credential helper isn't functioning unknowingly")]
176 EmptyCredentials,
177 #[error("Credentials provided for \"{url}\" were not accepted by the remote")]
178 InvalidCredentials { url: BString, source: std::io::Error },
179 #[error(transparent)]
180 Transport(#[from] client::Error),
181 #[error(
182 "The transport didn't accept the advertised server version {actual_version:?} and closed the connection client side"
183 )]
184 TransportProtocolPolicyViolation { actual_version: gix_transport::Protocol },
185 #[error(transparent)]
186 ParseRefs(#[from] refs::parse::Error),
187 }
188
189 impl gix_transport::IsSpuriousError for Error {
190 fn is_spurious(&self) -> bool {
191 match self {
192 Error::Transport(err) => err.is_spurious(),
193 _ => false,
194 }
195 }
196 }
197}
198
199#[cfg(feature = "handshake")]
200pub use error::Error;
201
202#[cfg(any(feature = "blocking-client", feature = "async-client"))]
203#[cfg(feature = "handshake")]
204pub(crate) mod function;