1 /*
2  *             Copyright David Nadlinger 2014.
3  *              Copyright Sönke Ludwig 2014.
4  *  Distributed under the Boost Software License, Version 1.0.
5  *     (See accompanying file LICENSE_1_0.txt or copy at
6  *           http://www.boost.org/LICENSE_1_0.txt)
7  */
8 module git.remote;
9 
10 import git.oid;
11 import git.repository;
12 import git.net;
13 import git.types;
14 import git.util;
15 import git.version_;
16 
17 import deimos.git2.net;
18 import deimos.git2.oid;
19 import deimos.git2.remote;
20 import deimos.git2.types;
21 
22 import std.conv : to;
23 import std.string : toStringz;
24 
25 
26 ///
27 enum GitDirection
28 {
29     ///
30     fetch = GIT_DIRECTION_FETCH,
31 
32     ///
33     push = GIT_DIRECTION_PUSH
34 }
35 
36 ///
37 enum GitRemoteAutotagOption
38 {
39     ///
40     automatic = 0,
41 
42     ///
43     none = 1,
44 
45     ///
46     all = 2
47 }
48 
49 ///
50 enum GitRemoteCompletionType {
51     download = GIT_REMOTE_COMPLETION_DOWNLOAD,
52     indexing = GIT_REMOTE_COMPLETION_INDEXING,
53     error = GIT_REMOTE_COMPLETION_ERROR,
54 }
55 
56 ///
57 struct GitRemoteCallbacks {
58     void delegate(string str) progress;
59     void delegate(GitRemoteCompletionType type) completion;
60     //void delegate(GitCred *cred, string url, string username_from_url, uint allowed_types) credentials;
61     TransferCallbackDelegate transferProgress;
62 }
63 
64 alias GitUpdateTipsDelegate = void delegate(string refname, in ref GitOid a, in ref GitOid b);
65 
66 
67 ///
68 struct GitRemote
69 {
70     // Internal, see free-standing constructor functions below.
71     private this(GitRepo repo, git_remote* remote)
72     {
73         _repo = repo;
74         _data = Data(remote);
75     }
76 
77     ///
78     void save()
79     {
80         require(git_remote_save(_data._payload) == 0);
81     }
82 
83     ///
84     @property string name() const
85     {
86         return to!string(git_remote_name(_data._payload));
87     }
88 
89     ///
90     @property string url() const
91     {
92         return to!string(git_remote_url(_data._payload));
93     }
94 
95     ///
96     @property void name(in char[] url)
97     {
98         require(git_remote_set_url(_data._payload, url.gitStr) == 0);
99     }
100 
101     ///
102     @property string pushURL() const
103     {
104         return to!string(git_remote_pushurl(_data._payload));
105     }
106 
107     ///
108     @property void pushURL(in char[] url)
109     {
110         require(git_remote_set_pushurl(_data._payload, url.gitStr) == 0);
111     }
112 
113     @property GitTransferProgress stats()
114     {
115         return GitTransferProgress(git_remote_stats(_data._payload));
116     }
117 
118     ///
119     void connect(GitDirection direction)
120     {
121         require(git_remote_connect(_data._payload, cast(git_direction)direction) == 0);
122     }
123 
124     ///
125     @property bool connected()
126     {
127         return git_remote_connected(_data._payload) != 0;
128     }
129 
130     ///
131     void stop()
132     {
133         git_remote_stop(_data._payload);
134     }
135 
136     ///
137     void disconnect()
138     {
139         git_remote_disconnect(_data._payload);
140     }
141 
142     ///
143     void download(TransferCallbackDelegate progressCallback)
144     {
145         GitRemoteCallbacks cb;
146         cb.transferProgress = progressCallback;
147         download(&cb);
148     }
149     ///
150     void download(GitRemoteCallbacks* callbacks = null)
151     {
152         assert(connected, "Must connect(GitDirection.push) before invoking download().");
153 
154         git_remote_callbacks gitcallbacks;
155         gitcallbacks.progress = &progress_cb;
156         gitcallbacks.completion = &completion_cb;
157         static if (targetLibGitVersion >= VersionInfo(0, 20, 0)) {
158             //gitcallbacks.credentials = &cred_acquire_cb;
159             gitcallbacks.transfer_progress = &transfer_progress_cb;
160         }
161         gitcallbacks.payload = cast(void*)callbacks;
162         require(git_remote_set_callbacks(_data._payload, &gitcallbacks) == 0);
163 
164         static if (targetLibGitVersion == VersionInfo(0, 19, 0)) {
165             require(git_remote_download(_data._payload, &transfer_progress_cb, cast(void*)callbacks) == 0);
166         } else {
167             require(git_remote_download(_data._payload) == 0);
168         }
169     }
170 
171     void addFetch(string refspec) { require(git_remote_add_fetch(this.cHandle, refspec.toStringz) == 0); }
172 
173     void updateTips(scope void delegate(string refname, in ref GitOid a, in ref GitOid b) updateTips)
174     {
175         static struct CTX { GitUpdateTipsDelegate updateTips; }
176 
177         static extern(C) nothrow int update_cb(const(char)* refname, const(git_oid)* a, const(git_oid)* b, void* payload)
178         {
179             auto cbs = cast(CTX*)payload;
180             if (cbs.updateTips) {
181                 try {
182                     auto ac = GitOid(*a);
183                     auto bc = GitOid(*b);
184                     cbs.updateTips(refname.to!string, ac, bc);
185                 } catch (Exception e) return -1;
186             }
187             return 0;
188         }
189 
190         CTX ctx;
191         ctx.updateTips = updateTips;
192 
193         git_remote_callbacks gitcallbacks;
194         gitcallbacks.update_tips = &update_cb;
195         gitcallbacks.payload = &ctx;
196         require(git_remote_set_callbacks(_data._payload, &gitcallbacks) == 0);
197         require(git_remote_update_tips(_data._payload) == 0);
198     }
199 
200     immutable(GitRemoteHead)[] ls()
201     {
202         static if (targetLibGitVersion == VersionInfo(0, 19, 0)) {
203             static struct CTX { immutable(GitRemoteHead)[] heads; }
204 
205             static extern(C) int callback(git_remote_head* rhead, void* payload) {
206                 auto ctx = cast(CTX*)payload;
207                 ctx.heads ~= GitRemoteHead(rhead);
208                 return 0;
209             }
210 
211             CTX ctx;
212             require(git_remote_ls(this.cHandle, &callback, &ctx) == 0);
213             return ctx.heads;
214         } else {
215             const(git_remote_head)** heads;
216             size_t head_count;
217             require(git_remote_ls(&heads, &head_count, _data._payload) == 0);
218             auto ret = new GitRemoteHead[head_count];
219             foreach (i, ref rh; ret) ret[i] = GitRemoteHead(heads[i]);
220             return cast(immutable)ret;
221         }
222     }
223 
224     mixin RefCountedGitObject!(git_remote, git_remote_free);
225     // Reference to the parent repository to keep it alive.
226     private GitRepo _repo;
227 }
228 
229 ///
230 GitRemote createRemote(GitRepo repo, in char[] name, in char[] url)
231 {
232     git_remote* result;
233     require(git_remote_create(&result, repo.cHandle, name.gitStr, url.gitStr) == 0);
234     return GitRemote(repo, result);
235 }
236 
237 ///
238 GitRemote createRemoteInMemory(GitRepo repo, in char[] fetch, in char[] url)
239 {
240     git_remote* result;
241     require(git_remote_create_inmemory(&result, repo.cHandle, fetch.gitStr, url.gitStr) == 0);
242     return GitRemote(repo, result);
243 }
244 
245 ///
246 GitRemote loadRemote(GitRepo repo, in char[] name)
247 {
248     git_remote* result;
249     require(git_remote_load(&result, repo.cHandle, name.gitStr) == 0);
250     return GitRemote(repo, result);
251 }
252 
253 
254 private extern(C) nothrow {
255     static if (targetLibGitVersion == VersionInfo(0, 19, 0)) {
256         void progress_cb(const(char)* str, int len, void* payload)
257         {
258             auto cbs = cast(GitRemoteCallbacks*)payload;
259             if (cbs && cbs.progress) {
260                 try cbs.progress(str[0 .. len].idup);
261                 catch (Exception e) {} // FIXME: store exception and skip calling the callback during the next iterations
262             }
263         }
264     } else {
265         int progress_cb(const(char)* str, int len, void* payload)
266         {
267             auto cbs = cast(GitRemoteCallbacks*)payload;
268             if (cbs && cbs.progress) {
269                 try cbs.progress(str[0 .. len].idup);
270                 catch (Exception e) return -1; // FIXME: store and rethrow exception 
271             }
272             return 0;
273         }
274     }
275 
276     /*int cred_acquire_cb(git_cred** dst, const(char)* url, const(char)* username_from_url, uint allowed_types, void* payload)
277     {
278         auto cbs = cast(GitRemoteCallbacks)payload;
279         try cbs.credentials(...);
280         catch (Exception e) return -1; // FIXME: store and rethrow exception 
281         return 0;
282     }*/
283 
284     int completion_cb(git_remote_completion_type type, void* payload)
285     {
286         auto cbs = cast(GitRemoteCallbacks*)payload;
287         if (cbs && cbs.completion) {
288             try cbs.completion(cast(GitRemoteCompletionType)type);
289             catch (Exception e) return -1; // FIXME: store and rethrow exception 
290         }
291         return 0;
292     }
293 
294     int transfer_progress_cb(const(git_transfer_progress)* stats, void* payload)
295     {
296         auto cbs = cast(GitRemoteCallbacks*)payload;
297         if (cbs && cbs.transferProgress) {
298             try {
299                 auto tp = GitTransferProgress(stats);
300                 cbs.transferProgress(tp);
301             } catch (Exception e) return -1; // FIXME: store and rethrow exception 
302         }
303         return 0;
304     }
305 }
306 
307 /+ TODO: Port these.
308 
309 alias git_remote_rename_problem_cb = int function(const(char)* problematic_refspec, void *payload);
310 
311 int git_remote_add_fetch(git_remote *remote, const(char)* refspec);
312 
313 int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
314 
315 int git_remote_add_push(git_remote *remote, const(char)* refspec);
316 
317 int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote);
318 
319 void git_remote_clear_refspecs(git_remote *remote);
320 
321 size_t git_remote_refspec_count(git_remote *remote);
322 
323 const(git_refspec)* git_remote_get_refspec(git_remote *remote, size_t n);
324 
325 int git_remote_remove_refspec(git_remote *remote, size_t n);
326 
327 int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload);
328 
329 int git_remote_update_tips(git_remote *remote);
330 
331 int git_remote_valid_url(const(char)* url);
332 
333 int git_remote_supported_url(const(char)* url);
334 
335 int git_remote_list(git_strarray *out_, git_repository *repo);
336 
337 void git_remote_check_cert(git_remote *remote, int check);
338 
339 void git_remote_set_cred_acquire_cb(
340     git_remote *remote,
341     git_cred_acquire_cb cred_acquire_cb,
342     void *payload);
343 
344 int git_remote_set_transport(
345     git_remote *remote,
346     git_transport *transport);
347 
348 enum git_remote_completion_type {
349     GIT_REMOTE_COMPLETION_DOWNLOAD,
350     GIT_REMOTE_COMPLETION_INDEXING,
351     GIT_REMOTE_COMPLETION_ERROR,
352 } ;
353 
354 mixin _ExportEnumMembers!git_remote_completion_type;
355 
356 struct git_remote_callbacks {
357     uint version_ = GIT_REMOTE_CALLBACKS_VERSION;
358     void function(const(char)* str, int len, void *data) progress;
359     int function(git_remote_completion_type type, void *data) completion;
360     int function(const(char)* refname, const(git_oid)* a, const(git_oid)* b, void *data) update_tips;
361     void *payload;
362 }
363 
364 enum GIT_REMOTE_CALLBACKS_VERSION = 1;
365 enum git_remote_callbacks GIT_REMOTE_CALLBACKS_INIT = { GIT_REMOTE_CALLBACKS_VERSION };
366 
367 int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks);
368 
369 const(git_transfer_progress)*  git_remote_stats(git_remote *remote);
370 
371 enum git_remote_autotag_option_t {
372     GIT_REMOTE_DOWNLOAD_TAGS_AUTO = 0,
373     GIT_REMOTE_DOWNLOAD_TAGS_NONE = 1,
374     GIT_REMOTE_DOWNLOAD_TAGS_ALL = 2
375 } ;
376 
377 mixin _ExportEnumMembers!git_remote_autotag_option_t;
378 
379 git_remote_autotag_option_t git_remote_autotag(git_remote *remote);
380 
381 void git_remote_set_autotag(
382     git_remote *remote,
383     git_remote_autotag_option_t value);
384 
385 int git_remote_rename(
386     git_remote *remote,
387     const(char)* new_name,
388     git_remote_rename_problem_cb callback,
389     void *payload);
390 
391 int git_remote_update_fetchhead(git_remote *remote);
392 
393 void git_remote_set_update_fetchhead(git_remote *remote, int value);
394 
395 int git_remote_is_valid_name(const(char)* remote_name);
396 
397 +/