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 #[cfg(feature = "async-client")]
75 use crate::transport::client::async_io;
76 #[cfg(feature = "blocking-client")]
77 use crate::transport::client::blocking_io;
78 use crate::{fetch::RefMap, Handshake};
79 use gix_features::progress::Progress;
80 use std::borrow::Cow;
81
82 pub enum ObtainRefMap<'a> {
84 Existing(RefMap),
86 LsRefsCommand(crate::LsRefsCommand<'a>, crate::fetch::refmap::init::Context),
88 }
89
90 impl ObtainRefMap<'_> {
91 #[cfg(feature = "async-client")]
93 #[allow(clippy::result_large_err)]
94 pub async fn fetch_async(
95 self,
96 mut progress: impl Progress,
97 transport: &mut impl async_io::Transport,
98 trace_packetlines: bool,
99 ) -> Result<RefMap, crate::fetch::refmap::init::Error> {
100 let (cmd, cx) = match self {
101 ObtainRefMap::Existing(map) => return Ok(map),
102 ObtainRefMap::LsRefsCommand(cmd, cx) => (cmd, cx),
103 };
104
105 let _span = gix_trace::coarse!("gix_protocol::handshake::ObtainRefMap::fetch_async()");
106 let capabilities = cmd.capabilities;
107 let remote_refs = cmd.invoke_async(transport, &mut progress, trace_packetlines).await?;
108 RefMap::from_refs(remote_refs, capabilities, cx)
109 }
110
111 #[cfg(feature = "blocking-client")]
113 #[allow(clippy::result_large_err)]
114 pub fn fetch_blocking(
115 self,
116 mut progress: impl Progress,
117 transport: &mut impl blocking_io::Transport,
118 trace_packetlines: bool,
119 ) -> Result<RefMap, crate::fetch::refmap::init::Error> {
120 let (cmd, cx) = match self {
121 ObtainRefMap::Existing(map) => return Ok(map),
122 ObtainRefMap::LsRefsCommand(cmd, cx) => (cmd, cx),
123 };
124
125 let _span = gix_trace::coarse!("gix_protocol::handshake::ObtainRefMap::fetch_blocking()");
126 let capabilities = cmd.capabilities;
127 let remote_refs = cmd.invoke_blocking(transport, &mut progress, trace_packetlines)?;
128 RefMap::from_refs(remote_refs, capabilities, cx)
129 }
130 }
131
132 impl Handshake {
133 #[allow(clippy::result_large_err)]
135 pub fn prepare_lsrefs_or_extract_refmap(
136 &mut self,
137 user_agent: (&'static str, Option<Cow<'static, str>>),
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 all_refspecs = refmap_context.aggregate_refspecs();
150 let prefix_refspecs = prefix_from_spec_as_filter_on_remote.then_some(&all_refspecs[..]);
151 Ok(ObtainRefMap::LsRefsCommand(
152 crate::LsRefsCommand::new(prefix_refspecs, &self.capabilities, user_agent),
153 refmap_context,
154 ))
155 }
156 }
157 }
158}
159
160#[cfg(feature = "handshake")]
161mod error {
162 use bstr::BString;
163 use gix_transport::client;
164
165 use crate::{credentials, handshake::refs};
166
167 #[derive(Debug, thiserror::Error)]
169 #[allow(missing_docs)]
170 pub enum Error {
171 #[error("Failed to obtain credentials")]
172 Credentials(#[from] credentials::protocol::Error),
173 #[error("No credentials were returned at all as if the credential helper isn't functioning unknowingly")]
174 EmptyCredentials,
175 #[error("Credentials provided for \"{url}\" were not accepted by the remote")]
176 InvalidCredentials { url: BString, source: std::io::Error },
177 #[error(transparent)]
178 Transport(#[from] client::Error),
179 #[error("The transport didn't accept the advertised server version {actual_version:?} and closed the connection client side")]
180 TransportProtocolPolicyViolation { actual_version: gix_transport::Protocol },
181 #[error(transparent)]
182 ParseRefs(#[from] refs::parse::Error),
183 }
184
185 impl gix_transport::IsSpuriousError for Error {
186 fn is_spurious(&self) -> bool {
187 match self {
188 Error::Transport(err) => err.is_spurious(),
189 _ => false,
190 }
191 }
192 }
193}
194
195#[cfg(feature = "handshake")]
196pub use error::Error;
197
198#[cfg(any(feature = "blocking-client", feature = "async-client"))]
199#[cfg(feature = "handshake")]
200pub(crate) mod function;