]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
1 | use std::{ |
2 | borrow::Cow, | |
3 | io, | |
4 | ops::{Deref, DerefMut}, | |
5 | }; | |
6 | ||
7 | use bstr::BString; | |
8 | use gix_transport::client::Capabilities; | |
9 | ||
10 | use crate::{ | |
11 | fetch::{Arguments, Response}, | |
12 | handshake::Ref, | |
13 | }; | |
14 | ||
15 | /// Defines what to do next after certain [`Delegate`] operations. | |
16 | #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] | |
17 | pub enum Action { | |
18 | /// Continue the typical flow of operations in this flow. | |
19 | Continue, | |
20 | /// Return at the next possible opportunity without making further requests, possibly after closing the connection. | |
21 | Cancel, | |
22 | } | |
23 | ||
24 | /// The non-IO protocol delegate is the bare minimal interface needed to fully control the [`fetch`][crate::fetch()] operation, sparing | |
25 | /// the IO parts. | |
26 | /// Async implementations must treat it as blocking and unblock it by evaluating it elsewhere. | |
27 | /// | |
28 | /// See [Delegate] for the complete trait. | |
29 | pub trait DelegateBlocking { | |
30 | /// Return extra parameters to be provided during the handshake. | |
31 | /// | |
32 | /// Note that this method is only called once and the result is reused during subsequent handshakes which may happen | |
33 | /// if there is an authentication failure. | |
34 | fn handshake_extra_parameters(&self) -> Vec<(String, Option<String>)> { | |
35 | Vec::new() | |
36 | } | |
37 | /// Called before invoking 'ls-refs' on the server to allow providing it with additional `arguments` and to enable `features`. | |
38 | /// If the server `capabilities` don't match the requirements abort with an error to abort the entire fetch operation. | |
39 | /// | |
40 | /// Note that some arguments are preset based on typical use, and `features` are preset to maximize options. | |
41 | /// The `server` capabilities can be used to see which additional capabilities the server supports as per the handshake which happened prior. | |
42 | /// | |
43 | /// If the delegate returns [`ls_refs::Action::Skip`], no 'ls-refs` command is sent to the server. | |
44 | /// | |
45 | /// Note that this is called only if we are using protocol version 2. | |
46 | fn prepare_ls_refs( | |
47 | &mut self, | |
48 | _server: &Capabilities, | |
49 | _arguments: &mut Vec<BString>, | |
50 | _features: &mut Vec<(&str, Option<Cow<'_, str>>)>, | |
51 | ) -> std::io::Result<ls_refs::Action> { | |
52 | Ok(ls_refs::Action::Continue) | |
53 | } | |
54 | ||
55 | /// Called before invoking the 'fetch' interaction with `features` pre-filled for typical use | |
56 | /// and to maximize capabilities to allow aborting an interaction early. | |
57 | /// | |
58 | /// `refs` is a list of known references on the remote based on the handshake or a prior call to ls_refs. | |
59 | /// These can be used to abort early in case the refs are already known here. | |
60 | /// | |
61 | /// As there will be another call allowing to post arguments conveniently in the correct format, i.e. `want hex-oid`, | |
62 | /// there is no way to set arguments at this time. | |
63 | /// | |
64 | /// `version` is the actually supported version as reported by the server, which is relevant in case the server requested a downgrade. | |
65 | /// `server` capabilities is a list of features the server supports for your information, along with enabled `features` that the server knows about. | |
66 | fn prepare_fetch( | |
67 | &mut self, | |
68 | _version: gix_transport::Protocol, | |
69 | _server: &Capabilities, | |
70 | _features: &mut Vec<(&str, Option<Cow<'_, str>>)>, | |
71 | _refs: &[Ref], | |
72 | ) -> std::io::Result<Action> { | |
73 | Ok(Action::Continue) | |
74 | } | |
75 | ||
76 | /// A method called repeatedly to negotiate the objects to receive in [`receive_pack(…)`][Delegate::receive_pack()]. | |
77 | /// | |
78 | /// The first call has `previous_response` set to `None` as there was no previous response. Every call that follows `previous_response` | |
79 | /// will be set to `Some`. | |
80 | /// | |
81 | /// ### If `previous_response` is `None`… | |
82 | /// | |
83 | /// Given a list of `arguments` to populate with wants, want-refs, shallows, filters and other contextual information to be | |
84 | /// sent to the server. This method is called once. | |
85 | /// Send the objects you `have` have afterwards based on the tips of your refs, in preparation to walk down their parents | |
86 | /// with each call to `negotiate` to find the common base(s). | |
87 | /// | |
88 | /// Note that you should not `want` and object that you already have. | |
49aad941 | 89 | /// `refs` are the tips of on the server side, effectively the latest objects _they_ have. |
0a29b90c FG |
90 | /// |
91 | /// Return `Action::Close` if you know that there are no `haves` on your end to allow the server to send all of its objects | |
92 | /// as is the case during initial clones. | |
93 | /// | |
94 | /// ### If `previous_response` is `Some`… | |
95 | /// | |
96 | /// Populate `arguments` with the objects you `have` starting from the tips of _your_ refs, taking into consideration | |
97 | /// the `previous_response` response of the server to see which objects they acknowledged to have. You have to maintain | |
98 | /// enough state to be able to walk down from your tips on each call, if they are not in common, and keep setting `have` | |
99 | /// for those which are in common if that helps teaching the server about our state and to acknowledge their existence on _their_ end. | |
100 | /// This method is called until the other side signals they are ready to send a pack. | |
101 | /// Return `Action::Close` if you want to give up before finding a common base. This can happen if the remote repository | |
102 | /// has radically changed so there are no bases, or they are very far in the past, causing all objects to be sent. | |
103 | fn negotiate( | |
104 | &mut self, | |
105 | refs: &[Ref], | |
106 | arguments: &mut Arguments, | |
107 | previous_response: Option<&Response>, | |
108 | ) -> io::Result<Action>; | |
109 | } | |
110 | ||
111 | impl<T: DelegateBlocking> DelegateBlocking for Box<T> { | |
112 | fn handshake_extra_parameters(&self) -> Vec<(String, Option<String>)> { | |
113 | self.deref().handshake_extra_parameters() | |
114 | } | |
115 | ||
116 | fn prepare_ls_refs( | |
117 | &mut self, | |
118 | _server: &Capabilities, | |
119 | _arguments: &mut Vec<BString>, | |
120 | _features: &mut Vec<(&str, Option<Cow<'_, str>>)>, | |
121 | ) -> io::Result<ls_refs::Action> { | |
122 | self.deref_mut().prepare_ls_refs(_server, _arguments, _features) | |
123 | } | |
124 | ||
125 | fn prepare_fetch( | |
126 | &mut self, | |
127 | _version: gix_transport::Protocol, | |
128 | _server: &Capabilities, | |
129 | _features: &mut Vec<(&str, Option<Cow<'_, str>>)>, | |
130 | _refs: &[Ref], | |
131 | ) -> io::Result<Action> { | |
132 | self.deref_mut().prepare_fetch(_version, _server, _features, _refs) | |
133 | } | |
134 | ||
135 | fn negotiate( | |
136 | &mut self, | |
137 | refs: &[Ref], | |
138 | arguments: &mut Arguments, | |
139 | previous_response: Option<&Response>, | |
140 | ) -> io::Result<Action> { | |
141 | self.deref_mut().negotiate(refs, arguments, previous_response) | |
142 | } | |
143 | } | |
144 | ||
145 | impl<T: DelegateBlocking> DelegateBlocking for &mut T { | |
146 | fn handshake_extra_parameters(&self) -> Vec<(String, Option<String>)> { | |
147 | self.deref().handshake_extra_parameters() | |
148 | } | |
149 | ||
150 | fn prepare_ls_refs( | |
151 | &mut self, | |
152 | _server: &Capabilities, | |
153 | _arguments: &mut Vec<BString>, | |
154 | _features: &mut Vec<(&str, Option<Cow<'_, str>>)>, | |
155 | ) -> io::Result<ls_refs::Action> { | |
156 | self.deref_mut().prepare_ls_refs(_server, _arguments, _features) | |
157 | } | |
158 | ||
159 | fn prepare_fetch( | |
160 | &mut self, | |
161 | _version: gix_transport::Protocol, | |
162 | _server: &Capabilities, | |
163 | _features: &mut Vec<(&str, Option<Cow<'_, str>>)>, | |
164 | _refs: &[Ref], | |
165 | ) -> io::Result<Action> { | |
166 | self.deref_mut().prepare_fetch(_version, _server, _features, _refs) | |
167 | } | |
168 | ||
169 | fn negotiate( | |
170 | &mut self, | |
171 | refs: &[Ref], | |
172 | arguments: &mut Arguments, | |
173 | previous_response: Option<&Response>, | |
174 | ) -> io::Result<Action> { | |
175 | self.deref_mut().negotiate(refs, arguments, previous_response) | |
176 | } | |
177 | } | |
178 | ||
179 | #[cfg(feature = "blocking-client")] | |
180 | mod blocking_io { | |
181 | use std::{ | |
182 | io::{self, BufRead}, | |
183 | ops::DerefMut, | |
184 | }; | |
185 | ||
186 | use gix_features::progress::Progress; | |
187 | ||
188 | use crate::{ | |
189 | fetch::{DelegateBlocking, Response}, | |
190 | handshake::Ref, | |
191 | }; | |
192 | ||
193 | /// The protocol delegate is the bare minimal interface needed to fully control the [`fetch`][crate::fetch()] operation. | |
194 | /// | |
195 | /// Implementations of this trait are controlled by code with intricate knowledge about how fetching works in protocol version V1 and V2, | |
196 | /// so you don't have to. | |
197 | /// Everything is tucked away behind type-safety so 'nothing can go wrong'©. Runtime assertions assure invalid | |
198 | /// features or arguments don't make it to the server in the first place. | |
199 | /// Please note that this trait mostly corresponds to what V2 would look like, even though V1 is supported as well. | |
200 | pub trait Delegate: DelegateBlocking { | |
201 | /// Receive a pack provided from the given `input`. | |
202 | /// | |
203 | /// Use `progress` to emit your own progress messages when decoding the pack. | |
204 | /// | |
205 | /// `refs` of the remote side are provided for convenience, along with the parsed `previous_response` response in case you want | |
206 | /// to check additional acks. | |
207 | fn receive_pack( | |
208 | &mut self, | |
209 | input: impl io::BufRead, | |
210 | progress: impl Progress, | |
211 | refs: &[Ref], | |
212 | previous_response: &Response, | |
213 | ) -> io::Result<()>; | |
214 | } | |
215 | ||
216 | impl<T: Delegate> Delegate for Box<T> { | |
217 | fn receive_pack( | |
218 | &mut self, | |
219 | input: impl BufRead, | |
220 | progress: impl Progress, | |
221 | refs: &[Ref], | |
222 | previous_response: &Response, | |
223 | ) -> io::Result<()> { | |
224 | self.deref_mut().receive_pack(input, progress, refs, previous_response) | |
225 | } | |
226 | } | |
227 | ||
228 | impl<T: Delegate> Delegate for &mut T { | |
229 | fn receive_pack( | |
230 | &mut self, | |
231 | input: impl BufRead, | |
232 | progress: impl Progress, | |
233 | refs: &[Ref], | |
234 | previous_response: &Response, | |
235 | ) -> io::Result<()> { | |
236 | self.deref_mut().receive_pack(input, progress, refs, previous_response) | |
237 | } | |
238 | } | |
239 | } | |
240 | #[cfg(feature = "blocking-client")] | |
241 | pub use blocking_io::Delegate; | |
242 | ||
243 | #[cfg(feature = "async-client")] | |
244 | mod async_io { | |
245 | use std::{io, ops::DerefMut}; | |
246 | ||
247 | use async_trait::async_trait; | |
248 | use futures_io::AsyncBufRead; | |
249 | use gix_features::progress::Progress; | |
250 | ||
251 | use crate::{ | |
252 | fetch::{DelegateBlocking, Response}, | |
253 | handshake::Ref, | |
254 | }; | |
255 | ||
256 | /// The protocol delegate is the bare minimal interface needed to fully control the [`fetch`][crate::fetch()] operation. | |
257 | /// | |
258 | /// Implementations of this trait are controlled by code with intricate knowledge about how fetching works in protocol version V1 and V2, | |
259 | /// so you don't have to. | |
260 | /// Everything is tucked away behind type-safety so 'nothing can go wrong'©. Runtime assertions assure invalid | |
261 | /// features or arguments don't make it to the server in the first place. | |
262 | /// Please note that this trait mostly corresponds to what V2 would look like, even though V1 is supported as well. | |
263 | #[async_trait(?Send)] | |
264 | pub trait Delegate: DelegateBlocking { | |
265 | /// Receive a pack provided from the given `input`, and the caller should consider it to be blocking as | |
266 | /// most operations on the received pack are implemented in a blocking fashion. | |
267 | /// | |
268 | /// Use `progress` to emit your own progress messages when decoding the pack. | |
269 | /// | |
270 | /// `refs` of the remote side are provided for convenience, along with the parsed `previous_response` response in case you want | |
271 | /// to check additional acks. | |
272 | async fn receive_pack( | |
273 | &mut self, | |
274 | input: impl AsyncBufRead + Unpin + 'async_trait, | |
275 | progress: impl Progress, | |
276 | refs: &[Ref], | |
277 | previous_response: &Response, | |
278 | ) -> io::Result<()>; | |
279 | } | |
280 | #[async_trait(?Send)] | |
281 | impl<T: Delegate> Delegate for Box<T> { | |
282 | async fn receive_pack( | |
283 | &mut self, | |
284 | input: impl AsyncBufRead + Unpin + 'async_trait, | |
285 | progress: impl Progress, | |
286 | refs: &[Ref], | |
287 | previous_response: &Response, | |
288 | ) -> io::Result<()> { | |
289 | self.deref_mut() | |
290 | .receive_pack(input, progress, refs, previous_response) | |
291 | .await | |
292 | } | |
293 | } | |
294 | ||
295 | #[async_trait(?Send)] | |
296 | impl<T: Delegate> Delegate for &mut T { | |
297 | async fn receive_pack( | |
298 | &mut self, | |
299 | input: impl AsyncBufRead + Unpin + 'async_trait, | |
300 | progress: impl Progress, | |
301 | refs: &[Ref], | |
302 | previous_response: &Response, | |
303 | ) -> io::Result<()> { | |
304 | self.deref_mut() | |
305 | .receive_pack(input, progress, refs, previous_response) | |
306 | .await | |
307 | } | |
308 | } | |
309 | } | |
310 | #[cfg(feature = "async-client")] | |
311 | pub use async_io::Delegate; | |
312 | ||
313 | use crate::ls_refs; |