Basic tag search and range
parent: tbd commit: 112ca96
1 | use Error; |
2 | use async_trait; |
3 | |
4 | use BranchType; |
5 | use ; |
6 | |
7 | use Object; |
8 | use |
9 | AccessList, BranchStaleAfter, Commit, CommitSignature, DefaultBranch, Description, IssueLabel, |
10 | Repository, RepositoryBranch, RepositoryBranchFilter, RepositoryBranchRequest, |
11 | RepositoryBranchesRequest, RepositoryChunkLine, RepositoryCommitBeforeRequest, |
12 | RepositoryCommitFromIdRequest, RepositoryDiff, RepositoryDiffFile, RepositoryDiffFileChunk, |
13 | RepositoryDiffFileInfo, RepositoryDiffFileStatus, RepositoryDiffPatchRequest, |
14 | RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, |
15 | RepositoryFileFromPathRequest, RepositoryFileInspectRequest, RepositoryIssue, |
16 | RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, |
17 | RepositoryLastCommitOfFileRequest, RepositoryObjectType, RepositoryStatistics, |
18 | RepositoryStatisticsRequest, RepositoryTag, RepositoryTagsRequest, RepositoryTreeEntry, |
19 | RepositoryVisibility, Visibility, |
20 | ; |
21 | |
22 | use User; |
23 | |
24 | use ; |
25 | |
26 | use PgPool; |
27 | use Deref; |
28 | use |
29 | , |
30 | , | Arc
31 | ; |
32 | use Error; |
33 | use OnceCell; |
34 | |
35 | use ; |
36 | |
37 | // TODO: Handle this |
38 | //region database structures |
39 | |
40 | /// Repository in the database |
41 | |
42 | |
43 | #[sqlx(try_from = "String")] |
44 | pub owner_user: User, |
45 | pub name: String, |
46 | pub description: , |
47 | pub visibility: RepositoryVisibility, |
48 | pub default_branch: String, |
49 | |
50 | |
51 | |
52 | // Separate function because "Private" will be expanded later |
53 | /// Checks if the user is allowed to view this repository |
54 | pub async |
55 | &self, |
56 | our_instance: &Instance, |
57 | user: & , |
58 | stack: &GiteratedStack, |
59 | |
60 | if matches! |
61 | return true; |
62 | |
63 | |
64 | // User must exist for any further checks to pass |
65 | let user = match user |
66 | Some => user, |
67 | None => return false, |
68 | ; |
69 | |
70 | if *user.deref == self.owner_user |
71 | // owner can always view |
72 | return true; |
73 | |
74 | |
75 | if matches! |
76 | // Check if the user can view\ |
77 | let access_list = stack |
78 | . |
79 | owner: self.owner_user.clone, |
80 | name: self.name.clone, |
81 | instance: our_instance.clone, |
82 | |
83 | .await |
84 | .unwrap; |
85 | |
86 | access_list |
87 | .0 |
88 | .iter |
89 | .any |
90 | else |
91 | false |
92 | |
93 | |
94 | |
95 | // This is in it's own function because I assume I'll have to add logic to this later |
96 | |
97 | &self, |
98 | repository_directory: &str, |
99 | |
100 | match open |
101 | "{}/{}/{}/{}" |
102 | repository_directory, self.owner_user.instance, self.owner_user.username, self.name |
103 | ) |
104 | Ok => Ok, |
105 | Err => |
106 | let err = FailedOpeningFromDisk; |
107 | error!; |
108 | |
109 | Err |
110 | |
111 | |
112 | |
113 | |
114 | |
115 | //endregion |
116 | |
117 | |
118 | |
119 | |
120 | FailedCreatingRepository, |
121 | |
122 | FailedInsertingIntoDatabase, |
123 | |
124 | RepositoryNotFound , |
125 | |
126 | RepositoryAlreadyExists , |
127 | |
128 | CouldNotDeleteFromDisk, |
129 | |
130 | FailedDeletingFromDatabase, |
131 | |
132 | FailedOpeningFromDisk, |
133 | |
134 | RefNotFound, |
135 | |
136 | HeadNotFound, |
137 | |
138 | DefaultNotFound, |
139 | |
140 | PathNotFound, |
141 | |
142 | BranchNotFound, |
143 | |
144 | LastCommitNotFound, |
145 | |
146 | InvalidObjectId, |
147 | |
148 | BlobNotFound, |
149 | |
150 | TreeNotFound, |
151 | |
152 | CommitNotFound, |
153 | |
154 | CommitParentNotFound, |
155 | |
156 | FailedDiffing, |
157 | |
158 | |
159 | |
160 | pg_pool: PgPool, |
161 | repository_folder: String, |
162 | instance: Instance, |
163 | stack: , |
164 | |
165 | |
166 | |
167 | |
168 | pg_pool: &PgPool, |
169 | repository_folder: &str, |
170 | instance: impl , |
171 | stack: , |
172 | |
173 | let instance = instance.to_owned; |
174 | |
175 | Self |
176 | pg_pool: pg_pool.clone, |
177 | // We make sure there's no end slash |
178 | repository_folder: repository_folder.trim_end_matches .to_string, |
179 | instance, |
180 | stack, |
181 | |
182 | |
183 | |
184 | pub async |
185 | &self, |
186 | user: &User, |
187 | repository_name: &str, |
188 | |
189 | // TODO: Patch for use with new GetValue system |
190 | if let Ok = query_as! |
191 | r#"SELECT owner_user, name, description, visibility as "visibility: _", default_branch FROM repositories WHERE owner_user = $1 AND name = $2"#, |
192 | user.to_string, repository_name |
193 | .fetch_one |
194 | .await |
195 | Ok |
196 | else |
197 | Err |
198 | owner_user: user.to_string, |
199 | name: repository_name.to_string, |
200 | |
201 | |
202 | |
203 | |
204 | pub async |
205 | &self, |
206 | user: &User, |
207 | repository_name: &str, |
208 | |
209 | if let Err = remove_dir_all |
210 | "{}/{}/{}/{}" |
211 | self.repository_folder, user.instance, user.username, repository_name |
212 | ) |
213 | let err = CouldNotDeleteFromDisk; |
214 | error! |
215 | "Couldn't delete repository from disk, this is bad! {:?}", |
216 | err |
217 | ; |
218 | |
219 | return Err; |
220 | |
221 | |
222 | // Delete the repository from the database |
223 | self.delete_from_database .await |
224 | |
225 | |
226 | /// Deletes the repository from the database |
227 | pub async |
228 | &self, |
229 | user: &User, |
230 | repository_name: &str, |
231 | |
232 | match query! |
233 | "DELETE FROM repositories WHERE owner_user = $1 AND name = $2", |
234 | user.to_string, |
235 | repository_name |
236 | |
237 | .execute |
238 | .await |
239 | |
240 | Ok => Ok, |
241 | Err => Err, |
242 | |
243 | |
244 | |
245 | pub async |
246 | &self, |
247 | owner: &User, |
248 | name: &str, |
249 | requester: & , |
250 | |
251 | let repository = match self |
252 | .find_by_owner_user_name |
253 | // &request.owner.instance.url, |
254 | owner, name, |
255 | |
256 | .await |
257 | |
258 | Ok => repository, |
259 | Err => return Err, |
260 | ; |
261 | |
262 | if let Some = requester |
263 | if !repository |
264 | .can_user_view_repository |
265 | &self.instance, |
266 | &Some, |
267 | self.stack.get .unwrap, |
268 | |
269 | .await |
270 | |
271 | return Err |
272 | owner_user: repository.owner_user.to_string, |
273 | name: repository.name.clone, |
274 | ; |
275 | |
276 | else if matches! |
277 | // Unauthenticated users can never view private repositories |
278 | |
279 | return Err |
280 | owner_user: repository.owner_user.to_string, |
281 | name: repository.name.clone, |
282 | ; |
283 | |
284 | |
285 | match repository.open_git2_repository |
286 | Ok => Ok, |
287 | Err => Err, |
288 | |
289 | |
290 | |
291 | // TODO: Find where this fits |
292 | // TODO: Cache this and general repository tree and invalidate select files on push |
293 | // TODO: Find better and faster technique for this |
294 | |
295 | path: &str, |
296 | git: & Repository, |
297 | start_commit: & Commit, |
298 | |
299 | trace!; |
300 | |
301 | let mut revwalk = git.revwalk?; |
302 | revwalk.set_sorting?; |
303 | revwalk.push?; |
304 | |
305 | for oid in revwalk |
306 | let oid = oid?; |
307 | let commit = git.find_commit?; |
308 | |
309 | // Merge commits have 2 or more parents |
310 | // Commits with 0 parents are handled different because we can't diff against them |
311 | if commit.parent_count == 0 |
312 | return Ok; |
313 | else if commit.parent_count == 1 |
314 | let tree = commit.tree?; |
315 | let last_tree = commit.parent?.tree?; |
316 | |
317 | // Get the diff between the current tree and the last one |
318 | let diff = git.diff_tree_to_tree?; |
319 | |
320 | for dd in diff.deltas |
321 | // Get the path of the current file we're diffing against |
322 | let current_path = dd.new_file .path .unwrap; |
323 | |
324 | // Path or directory |
325 | if current_path.eq || current_path.starts_with |
326 | return Ok; |
327 | |
328 | |
329 | |
330 | |
331 | |
332 | Err? |
333 | |
334 | |
335 | /// Gets the total amount of commits using revwalk |
336 | |
337 | git: & Repository, |
338 | start_commit: & Commit, |
339 | |
340 | // TODO: There must be a better way |
341 | let mut revwalk = git.revwalk?; |
342 | revwalk.set_sorting?; |
343 | revwalk.push?; |
344 | |
345 | Ok |
346 | |
347 | |
348 | /// Attempts to get the oid in this order: |
349 | /// 1. Full refname (refname_to_id) |
350 | /// 2. Short branch name (find_branch) |
351 | /// 3. Other (revparse_single) |
352 | |
353 | git: & Repository, |
354 | rev: , |
355 | |
356 | // TODO: Change to default branch instead of head |
357 | // If the rev is None try and get the repository head |
358 | let Some = rev else |
359 | if let Ok = git.head |
360 | // TODO: Fix for symbolic references |
361 | // TODO: unsafe unwrap? |
362 | return Ok; |
363 | else |
364 | // Nothing in database, render empty tree. |
365 | return Err; |
366 | |
367 | ; |
368 | |
369 | // TODO: This is far from ideal or speedy and would love for a better way to check this in the same order, but I can't find proper methods to do any of this. |
370 | |
371 | // Try getting it as a refname (refs/heads/name) |
372 | if let Ok = git.refname_to_id |
373 | Ok |
374 | // Try finding it as a short branch name |
375 | else if let Ok = git.find_branch |
376 | // SHOULD be safe to unwrap |
377 | Ok |
378 | // As last resort, try revparsing (will catch short oid and tags) |
379 | else if let Ok = git.revparse_single |
380 | Ok |
381 | else |
382 | Err |
383 | |
384 | |
385 | |
386 | /// Gets the last commit in a rev |
387 | |
388 | let oid = Self get_oid_from_reference?; |
389 | |
390 | // Walk through the repository commit graph starting at our rev |
391 | let mut revwalk = git.revwalk?; |
392 | revwalk.set_sorting?; |
393 | revwalk.push?; |
394 | |
395 | if let Some = revwalk.next |
396 | if let Ok = git |
397 | .find_commit |
398 | .map_err |
399 | |
400 | return Ok; |
401 | |
402 | |
403 | |
404 | Err |
405 | |
406 | |
407 | |
408 | |
409 | |
410 | async |
411 | &mut self, |
412 | requester: & , |
413 | repository: &Repository, |
414 | |
415 | if let Ok = self |
416 | .find_by_owner_user_name |
417 | .await |
418 | |
419 | Ok |
420 | .can_user_view_repository |
421 | .await |
422 | else |
423 | Ok |
424 | |
425 | |
426 | |
427 | async |
428 | &mut self, |
429 | _user: &AuthenticatedUser, |
430 | request: &RepositoryCreateRequest, |
431 | |
432 | // Check if repository already exists in the database |
433 | if let Ok = self |
434 | .find_by_owner_user_name |
435 | .await |
436 | |
437 | let err = RepositoryAlreadyExists |
438 | owner_user: repository.owner_user.to_string, |
439 | name: repository.name, |
440 | ; |
441 | error!; |
442 | |
443 | return Err; |
444 | |
445 | |
446 | // Insert the repository into the database |
447 | let _ = match query_as! |
448 | r#"INSERT INTO repositories VALUES ($1, $2, $3, $4, $5) RETURNING owner_user, name, description, visibility as "visibility: _", default_branch"#, |
449 | request.owner.to_string, request.name, request.description, request.visibility as _, "master" |
450 | .fetch_one |
451 | .await |
452 | Ok => repository, |
453 | Err => |
454 | let err = FailedInsertingIntoDatabase; |
455 | error!; |
456 | |
457 | return Err; |
458 | |
459 | ; |
460 | |
461 | // Create bare (server side) repository on disk |
462 | match init_bare |
463 | "{}/{}/{}/{}" |
464 | self.repository_folder, request.owner.instance, request.owner.username, request.name |
465 | ) |
466 | Ok => |
467 | debug! |
468 | "Created new repository with the name {}/{}/{}", |
469 | request.owner.instance, request.owner.username, request.name |
470 | ; |
471 | |
472 | let stack = self.stack.get .unwrap; |
473 | |
474 | let repository = Repository |
475 | owner: request.owner.clone, |
476 | name: request.name.clone, |
477 | instance: request.instance.as_ref .unwrap_or .clone, |
478 | ; |
479 | |
480 | stack |
481 | .write_setting |
482 | &repository, |
483 | Description, |
484 | |
485 | .await |
486 | .unwrap; |
487 | |
488 | stack |
489 | .write_setting |
490 | .await |
491 | .unwrap; |
492 | |
493 | stack |
494 | .write_setting |
495 | .await |
496 | .unwrap; |
497 | |
498 | Ok |
499 | |
500 | Err => |
501 | let err = FailedCreatingRepository; |
502 | error!; |
503 | |
504 | // Delete repository from database |
505 | self.delete_from_database |
506 | .await?; |
507 | |
508 | // ??? |
509 | Err |
510 | |
511 | |
512 | |
513 | |
514 | /// If the OID can't be found because there's no repository head, this will return an empty `Vec`. |
515 | async |
516 | &mut self, |
517 | requester: & , |
518 | repository: &Repository, |
519 | request: &RepositoryFileInspectRequest, |
520 | |
521 | let git = self |
522 | .open_repository_and_check_permissions |
523 | .await?; |
524 | |
525 | // Try and find the tree_id/branch |
526 | let tree_id = match Self get_oid_from_reference |
527 | Ok => oid, |
528 | Err => return Ok, |
529 | Err => return Err, |
530 | ; |
531 | |
532 | // Get the commit from the oid |
533 | let commit = match git.find_commit |
534 | Ok => commit, |
535 | // If the commit isn't found, it's generally safe to assume the tree is empty. |
536 | Err => return Ok, |
537 | ; |
538 | |
539 | // this is stupid |
540 | let rev = request.rev.clone .unwrap_or_else; |
541 | let mut current_path = rev.clone; |
542 | |
543 | // Get the commit tree |
544 | let git_tree = if let Some = &request.path |
545 | // Add it to our full path string |
546 | current_path.push_str; |
547 | // Get the specified path, return an error if it wasn't found. |
548 | let entry = match commit |
549 | .tree |
550 | .unwrap |
551 | .get_path |
552 | .map_err |
553 | |
554 | Ok => entry, |
555 | Err => return Err, |
556 | ; |
557 | // Turn the entry into a git tree |
558 | entry.to_object .unwrap .as_tree .unwrap .clone |
559 | else |
560 | commit.tree .unwrap |
561 | ; |
562 | |
563 | // Iterate over the git tree and collect it into our own tree types |
564 | let mut tree = git_tree |
565 | .iter |
566 | .map |
567 | let object_type = match entry.kind .unwrap |
568 | => Tree, | Tree
569 | => Blob, | Blob
570 | _ => unreachable!, |
571 | ; |
572 | let mut tree_entry = new |
573 | entry.id .to_string .as_str, |
574 | entry.name .unwrap, |
575 | object_type, |
576 | entry.filemode, |
577 | ; |
578 | |
579 | if request.extra_metadata |
580 | // Get the file size if It's a blob |
581 | let object = entry.to_object .unwrap; |
582 | if let Some = object.as_blob |
583 | tree_entry.size = Some; |
584 | |
585 | |
586 | // Get the path to the folder the file is in by removing the rev from current_path |
587 | let mut path = current_path.replace; |
588 | if path.starts_with |
589 | path.remove; |
590 | |
591 | |
592 | // Format it as the path + file name |
593 | let full_path = if path.is_empty |
594 | entry.name .unwrap .to_string |
595 | else |
596 | format! |
597 | ; |
598 | |
599 | // Get the last commit made to the entry |
600 | if let Ok = |
601 | get_last_commit_of_file |
602 | |
603 | tree_entry.last_commit = Some; |
604 | |
605 | |
606 | |
607 | tree_entry |
608 | |
609 | .; |
610 | |
611 | // Sort the tree alphabetically and with tree first |
612 | tree.sort_unstable_by_key; |
613 | tree.sort_unstable_by_key |
614 | Reverse |
615 | ; |
616 | |
617 | Ok |
618 | |
619 | |
620 | async |
621 | &mut self, |
622 | requester: & , |
623 | repository: &Repository, |
624 | request: &RepositoryFileFromIdRequest, |
625 | |
626 | let git = self |
627 | .open_repository_and_check_permissions |
628 | .await?; |
629 | |
630 | // Parse the passed object id |
631 | let oid = match from_str |
632 | Ok => oid, |
633 | Err => |
634 | return Err |
635 | |
636 | ; |
637 | |
638 | // Find the file and turn it into our own struct |
639 | let file = match git.find_blob |
640 | Ok => RepositoryFile |
641 | id: blob.id .to_string, |
642 | content: blob.content .to_vec, |
643 | binary: blob.is_binary, |
644 | size: blob.size, |
645 | , |
646 | Err => return Err, |
647 | ; |
648 | |
649 | Ok |
650 | |
651 | |
652 | async |
653 | &mut self, |
654 | requester: & , |
655 | repository: &Repository, |
656 | request: &RepositoryFileFromPathRequest, |
657 | |
658 | let git = self |
659 | .open_repository_and_check_permissions |
660 | .await?; |
661 | |
662 | let tree_id = Self get_oid_from_reference?; |
663 | |
664 | // unwrap might be dangerous? |
665 | // Get the commit from the oid |
666 | let commit = git.find_commit .unwrap; |
667 | |
668 | // this is stupid |
669 | let mut current_path = request.rev.clone .unwrap_or_else; |
670 | |
671 | // Add it to our full path string |
672 | current_path.push_str; |
673 | // Get the specified path, return an error if it wasn't found. |
674 | let entry = match commit |
675 | .tree |
676 | .unwrap |
677 | .get_path |
678 | .map_err |
679 | |
680 | Ok => entry, |
681 | Err => return Err, |
682 | ; |
683 | |
684 | // Find the file and turn it into our own struct |
685 | let file = match git.find_blob |
686 | Ok => RepositoryFile |
687 | id: blob.id .to_string, |
688 | content: blob.content .to_vec, |
689 | binary: blob.is_binary, |
690 | size: blob.size, |
691 | , |
692 | Err => |
693 | return Err |
694 | |
695 | ; |
696 | |
697 | Ok |
698 | |
699 | |
700 | async |
701 | &mut self, |
702 | requester: & , |
703 | repository: &Repository, |
704 | request: &RepositoryCommitFromIdRequest, |
705 | |
706 | let git = self |
707 | .open_repository_and_check_permissions |
708 | .await?; |
709 | |
710 | // Parse the passed object ids |
711 | let oid = from_str |
712 | .map_err?; |
713 | |
714 | // Get the commit from the oid |
715 | let commit = git |
716 | .find_commit |
717 | .map_err?; |
718 | |
719 | Ok |
720 | |
721 | |
722 | async |
723 | &mut self, |
724 | requester: & , |
725 | repository: &Repository, |
726 | request: &RepositoryLastCommitOfFileRequest, |
727 | |
728 | let git = self |
729 | .open_repository_and_check_permissions |
730 | .await?; |
731 | |
732 | // Parse the passed object ids |
733 | let oid = from_str |
734 | .map_err?; |
735 | |
736 | // Get the commit from the oid |
737 | let commit = git |
738 | .find_commit |
739 | .map_err?; |
740 | |
741 | // Find the last commit of the file |
742 | let commit = get_last_commit_of_file?; |
743 | |
744 | Ok |
745 | |
746 | |
747 | /// Returns zero for all statistics if an OID wasn't found |
748 | async |
749 | &mut self, |
750 | requester: & , |
751 | repository: &Repository, |
752 | request: &RepositoryStatisticsRequest, |
753 | |
754 | let git = self |
755 | .open_repository_and_check_permissions |
756 | .await?; |
757 | |
758 | let tree_id = match Self get_oid_from_reference |
759 | Ok => oid, |
760 | Err => return Ok, |
761 | ; |
762 | |
763 | // Count the amount of branches and tags |
764 | let mut branches = 0; |
765 | let mut tags = 0; |
766 | if let Ok = git.references |
767 | for reference in references.flatten |
768 | if reference.is_branch |
769 | branches += 1; |
770 | else if reference.is_tag |
771 | tags += 1; |
772 | |
773 | |
774 | |
775 | |
776 | // Attempt to get the commit from the oid |
777 | let commits = if let Ok = git.find_commit |
778 | // Get the total commit count if we found the tree oid commit |
779 | ? | get_total_commit_count
780 | else |
781 | 0 |
782 | ; |
783 | |
784 | Ok |
785 | commits, |
786 | branches, |
787 | tags, |
788 | |
789 | |
790 | |
791 | /// .0: List of branches filtering by passed requirements. |
792 | /// .1: Total amount of branches after being filtered |
793 | async |
794 | &mut self, |
795 | requester: & , |
796 | repository_object: &mut , |
797 | OperationState | : ,
798 | request: &RepositoryBranchesRequest, |
799 | |
800 | let repository: &Repository = repository_object.object; |
801 | let git = self |
802 | .open_repository_and_check_permissions |
803 | .await?; |
804 | |
805 | let default_branch_name = repository_object |
806 | . |
807 | .await? |
808 | .0; |
809 | let default_branch = git |
810 | .find_branch |
811 | .map_err?; |
812 | |
813 | // Get the stale(after X seconds) setting |
814 | let stale_after = repository_object |
815 | . |
816 | .await |
817 | .unwrap_or_default |
818 | .0; |
819 | |
820 | // Could be done better with the RepositoryBranchFilter::None check done beforehand. |
821 | let mut filtered_branches = git |
822 | .branches? |
823 | .filter_map |
824 | let branch = branch.ok?.0; |
825 | |
826 | let Some = branch.name .ok .flatten else |
827 | return None; |
828 | ; |
829 | |
830 | // TODO: Non UTF-8? |
831 | let Some = |
832 | ok | get_last_commit_in_rev .
833 | else |
834 | return None; |
835 | ; |
836 | |
837 | let stale = now |
838 | .naive_utc |
839 | .signed_duration_since |
840 | .num_seconds |
841 | > stale_after.into; |
842 | |
843 | // Filter based on if the branch is stale or not |
844 | if request.filter != None |
845 | |
846 | if stale && request.filter == Active |
847 | return None; |
848 | else if !stale && request.filter == Stale |
849 | return None; |
850 | |
851 | |
852 | |
853 | Some |
854 | |
855 | .; |
856 | |
857 | // Get the total amount of filtered branches |
858 | let branch_count = filtered_branches.len; |
859 | |
860 | if let Some = &request.search |
861 | // TODO: Caching |
862 | // Search by sorting using a simple fuzzy search algorithm |
863 | filtered_branches.sort_by |
864 | damerau_levenshtein |
865 | .cmp |
866 | ; |
867 | else |
868 | // Sort the branches by commit date |
869 | filtered_branches.sort_by; |
870 | |
871 | |
872 | // Go to the requested position |
873 | let mut filtered_branches = filtered_branches.iter .skip; |
874 | |
875 | let mut branches = vec!; |
876 | |
877 | // Iterate through the filtered branches using the passed range |
878 | for _ in request.range.0..request.range.1 |
879 | let Some = filtered_branches.next else |
880 | break; |
881 | ; |
882 | |
883 | // Get how many commits are ahead of and behind of the head |
884 | let ahead_behind_default = |
885 | if default_branch.get .target .is_some && branch.get .target .is_some |
886 | git.graph_ahead_behind |
887 | branch.get .target .unwrap, |
888 | default_branch.get .target .unwrap, |
889 | |
890 | .ok |
891 | else |
892 | None |
893 | ; |
894 | |
895 | branches.push |
896 | name: name.to_string, |
897 | stale: *stale, |
898 | last_commit: Some, |
899 | ahead_behind_default, |
900 | |
901 | |
902 | |
903 | Ok |
904 | |
905 | |
906 | async |
907 | &mut self, |
908 | requester: & , |
909 | repository_object: &mut , |
910 | OperationState | : ,
911 | request: &RepositoryBranchRequest, |
912 | |
913 | let repository: &Repository = repository_object.object; |
914 | let git = self |
915 | .open_repository_and_check_permissions |
916 | .await?; |
917 | |
918 | // TODO: Don't duplicate search when the default branch and the requested one are the same |
919 | // Get the default branch to compare against |
920 | let default_branch_name = repository_object |
921 | . |
922 | .await? |
923 | .0; |
924 | let default_branch = git |
925 | .find_branch |
926 | .map_err?; |
927 | |
928 | // Find the requested branch |
929 | let branch = git |
930 | .find_branch |
931 | .map_err?; |
932 | |
933 | // Get the stale(after X seconds) setting |
934 | let stale_after = repository_object |
935 | . |
936 | .await |
937 | .unwrap_or_default |
938 | .0; |
939 | |
940 | // TODO: Non UTF-8? |
941 | let last_commit = |
942 | ok; | get_last_commit_in_rev .
943 | |
944 | let stale = if let Some = last_commit |
945 | now |
946 | .naive_utc |
947 | .signed_duration_since |
948 | .num_seconds |
949 | > stale_after.into |
950 | else |
951 | // TODO: Make sure it's acceptable to return false here |
952 | false |
953 | ; |
954 | |
955 | // Get how many commits are ahead of and behind of the head |
956 | let ahead_behind_default = |
957 | if default_branch.get .target .is_some && branch.get .target .is_some |
958 | git.graph_ahead_behind |
959 | branch.get .target .unwrap, |
960 | default_branch.get .target .unwrap, |
961 | |
962 | .ok |
963 | else |
964 | None |
965 | ; |
966 | |
967 | Ok |
968 | name: request.name.clone, |
969 | stale, |
970 | last_commit, |
971 | ahead_behind_default, |
972 | |
973 | |
974 | |
975 | /// .0: List of tags in passed range |
976 | /// .1: Total amount of tags |
977 | async |
978 | &mut self, |
979 | requester: & , |
980 | repository_object: &mut , |
981 | OperationState | : ,
982 | request: &RepositoryTagsRequest, |
983 | |
984 | let repository: &Repository = repository_object.object; |
985 | let git = self |
986 | .open_repository_and_check_permissions |
987 | .await?; |
988 | |
989 | let mut tags = vec!; |
990 | |
991 | // Iterate over each tag |
992 | let _ = git.tag_foreach |
993 | // Get the name in utf8 |
994 | let name = String from_utf8_lossy .replacen; |
995 | |
996 | // Find the tag so we can get the messages attached if any |
997 | if let Ok = git.find_tag |
998 | // Get the tag message and split it into a summary and body |
999 | let = if let Some = tag.message |
1000 | // Iterate over the lines |
1001 | let mut lines = message |
1002 | .lines |
1003 | .map |
1004 | // Trim the whitespace for every line |
1005 | let mut whitespace_removed = String with_capacity; |
1006 | |
1007 | line.split_whitespace .for_each |
1008 | if !whitespace_removed.is_empty |
1009 | whitespace_removed.push; |
1010 | |
1011 | |
1012 | whitespace_removed.push_str; |
1013 | ; |
1014 | |
1015 | whitespace_removed |
1016 | |
1017 | .; |
1018 | |
1019 | let summary = Some; |
1020 | let body = if lines.is_empty |
1021 | None |
1022 | else |
1023 | Some |
1024 | ; |
1025 | |
1026 | |
1027 | else |
1028 | |
1029 | ; |
1030 | |
1031 | // Get the commit the tag is (possibly) pointing to |
1032 | let commit = tag |
1033 | .peel |
1034 | .map |
1035 | .ok |
1036 | .flatten |
1037 | .map; |
1038 | // Get the author of the tag |
1039 | let author: = tag.tagger .map; |
1040 | // Get the time the tag or pointed commit was created |
1041 | let time = if let Some = author |
1042 | Some |
1043 | else |
1044 | // Get possible commit time if the tag has no author time |
1045 | commit.as_ref .map |
1046 | ; |
1047 | |
1048 | tags.push |
1049 | id: id.to_string, |
1050 | name: name.to_string, |
1051 | summary, |
1052 | body, |
1053 | author, |
1054 | time, |
1055 | commit, |
1056 | ; |
1057 | else |
1058 | // Lightweight commit, we try and find the commit it's pointing to |
1059 | let commit = git.find_commit .ok .map; |
1060 | |
1061 | tags.push |
1062 | id: id.to_string, |
1063 | name: name.to_string, |
1064 | summary: None, |
1065 | body: None, |
1066 | author: None, |
1067 | time: commit.as_ref .map, |
1068 | commit, |
1069 | ; |
1070 | ; |
1071 | |
1072 | true |
1073 | ; |
1074 | |
1075 | // Get the total amount of tags |
1076 | let tag_count = tags.len; |
1077 | |
1078 | if let Some = &request.search |
1079 | // TODO: Caching |
1080 | // Search by sorting using a simple fuzzy search algorithm |
1081 | tags.sort_by |
1082 | damerau_levenshtein |
1083 | .cmp |
1084 | ; |
1085 | else |
1086 | // Sort the tags using their creation or pointer date |
1087 | tags.sort_by; |
1088 | |
1089 | |
1090 | // Get the requested range of tags |
1091 | let tags = tags |
1092 | .into_iter |
1093 | .skip |
1094 | .take |
1095 | .; |
1096 | |
1097 | Ok |
1098 | |
1099 | |
1100 | async |
1101 | &mut self, |
1102 | requester: & , |
1103 | repository: &Repository, |
1104 | request: &RepositoryDiffRequest, |
1105 | |
1106 | let git = self |
1107 | .open_repository_and_check_permissions |
1108 | .await?; |
1109 | |
1110 | // Parse the passed object ids |
1111 | let oid_old = from_str |
1112 | .map_err?; |
1113 | let oid_new = from_str |
1114 | .map_err?; |
1115 | |
1116 | // Get the ids associates commits |
1117 | let commit_old = git |
1118 | .find_commit |
1119 | .map_err?; |
1120 | let commit_new = git |
1121 | .find_commit |
1122 | .map_err?; |
1123 | |
1124 | // Get the commit trees |
1125 | let tree_old = commit_old |
1126 | .tree |
1127 | .map_err?; |
1128 | let tree_new = commit_new |
1129 | .tree |
1130 | .map_err?; |
1131 | |
1132 | // Diff the two trees against each other |
1133 | let diff = git |
1134 | .diff_tree_to_tree |
1135 | .map_err |
1136 | FailedDiffing |
1137 | ?; |
1138 | |
1139 | // Should be safe to unwrap? |
1140 | let stats = diff.stats .unwrap; |
1141 | let mut files: = vec!; |
1142 | |
1143 | diff.deltas .enumerate .for_each |
1144 | // Parse the old file info from the delta |
1145 | let old_file_info = match delta.old_file .exists |
1146 | true => Some |
1147 | id: delta.old_file .id .to_string, |
1148 | path: delta |
1149 | .old_file |
1150 | .path |
1151 | .unwrap |
1152 | .to_str |
1153 | .unwrap |
1154 | .to_string, |
1155 | size: delta.old_file .size, |
1156 | binary: delta.old_file .is_binary, |
1157 | , |
1158 | false => None, |
1159 | ; |
1160 | // Parse the new file info from the delta |
1161 | let new_file_info = match delta.new_file .exists |
1162 | true => Some |
1163 | id: delta.new_file .id .to_string, |
1164 | path: delta |
1165 | .new_file |
1166 | .path |
1167 | .unwrap |
1168 | .to_str |
1169 | .unwrap |
1170 | .to_string, |
1171 | size: delta.new_file .size, |
1172 | binary: delta.new_file .is_binary, |
1173 | , |
1174 | false => None, |
1175 | ; |
1176 | |
1177 | let mut chunks: = vec!; |
1178 | if let Some = from_diff .ok .flatten |
1179 | for chunk_num in 0..patch.num_hunks |
1180 | if let Ok = patch.hunk |
1181 | let mut lines: = vec!; |
1182 | |
1183 | for line_num in 0..chunk_num_lines |
1184 | if let Ok = patch.line_in_hunk |
1185 | if let Ok = String from_utf8 |
1186 | lines.push |
1187 | change_type: line.origin_value .into, |
1188 | content: line_utf8, |
1189 | old_line_num: line.old_lineno, |
1190 | new_line_num: line.new_lineno, |
1191 | ; |
1192 | |
1193 | |
1194 | continue; |
1195 | |
1196 | |
1197 | |
1198 | chunks.push |
1199 | header: String from_utf8 .ok, |
1200 | old_start: chunk.old_start, |
1201 | old_lines: chunk.old_lines, |
1202 | new_start: chunk.new_start, |
1203 | new_lines: chunk.new_lines, |
1204 | lines, |
1205 | ; |
1206 | |
1207 | |
1208 | ; |
1209 | |
1210 | let file = RepositoryDiffFile |
1211 | status: from, |
1212 | old_file_info, |
1213 | new_file_info, |
1214 | chunks, |
1215 | ; |
1216 | |
1217 | files.push; |
1218 | ; |
1219 | |
1220 | Ok |
1221 | new_commit: from, |
1222 | files_changed: stats.files_changed, |
1223 | insertions: stats.insertions, |
1224 | deletions: stats.deletions, |
1225 | files, |
1226 | |
1227 | |
1228 | |
1229 | async |
1230 | &mut self, |
1231 | requester: & , |
1232 | repository: &Repository, |
1233 | request: &RepositoryDiffPatchRequest, |
1234 | |
1235 | let git = self |
1236 | .open_repository_and_check_permissions |
1237 | .await?; |
1238 | |
1239 | // Parse the passed object ids |
1240 | let oid_old = from_str |
1241 | .map_err?; |
1242 | let oid_new = from_str |
1243 | .map_err?; |
1244 | |
1245 | // Get the ids associates commits |
1246 | let commit_old = git |
1247 | .find_commit |
1248 | .map_err?; |
1249 | let commit_new = git |
1250 | .find_commit |
1251 | .map_err?; |
1252 | |
1253 | // Get the commit trees |
1254 | let tree_old = commit_old |
1255 | .tree |
1256 | .map_err?; |
1257 | let tree_new = commit_new |
1258 | .tree |
1259 | .map_err?; |
1260 | |
1261 | // Diff the two trees against each other |
1262 | let diff = git |
1263 | .diff_tree_to_tree |
1264 | .map_err |
1265 | FailedDiffing |
1266 | ?; |
1267 | |
1268 | // Print the entire patch |
1269 | let mut patch = String new; |
1270 | |
1271 | diff.print |
1272 | match line.origin |
1273 | '+' | '-' | ' ' => patch.push, |
1274 | _ => |
1275 | |
1276 | patch.push_str; |
1277 | true |
1278 | |
1279 | .unwrap; |
1280 | |
1281 | Ok |
1282 | |
1283 | |
1284 | async |
1285 | &mut self, |
1286 | requester: & , |
1287 | repository: &Repository, |
1288 | request: &RepositoryCommitBeforeRequest, |
1289 | |
1290 | let git = self |
1291 | .open_repository_and_check_permissions |
1292 | .await?; |
1293 | |
1294 | // Parse the passed object id |
1295 | let oid = match from_str |
1296 | Ok => oid, |
1297 | Err => |
1298 | return Err |
1299 | |
1300 | ; |
1301 | |
1302 | // Find the commit using the parsed oid |
1303 | let commit = match git.find_commit |
1304 | Ok => commit, |
1305 | Err => return Err, |
1306 | ; |
1307 | |
1308 | // Get the first parent it has |
1309 | let parent = commit.parent; |
1310 | if let Ok = parent |
1311 | return Ok; |
1312 | else |
1313 | // TODO: See if can be done better |
1314 | // Walk through the repository commit graph starting at our current commit |
1315 | let mut revwalk = git.revwalk?; |
1316 | revwalk.set_sorting?; |
1317 | revwalk.push?; |
1318 | |
1319 | if let Some = revwalk.next |
1320 | // Find the commit using the parsed oid |
1321 | if let Ok = git.find_commit |
1322 | return Ok; |
1323 | |
1324 | |
1325 | |
1326 | Err |
1327 | |
1328 | |
1329 | |
1330 | |
1331 | |
1332 | |
1333 | &mut self, |
1334 | _requester: & , |
1335 | _request: &RepositoryIssuesCountRequest, |
1336 | |
1337 | todo! |
1338 | |
1339 | |
1340 | |
1341 | &mut self, |
1342 | _requester: & , |
1343 | _request: &RepositoryIssueLabelsRequest, |
1344 | |
1345 | todo! |
1346 | |
1347 | |
1348 | |
1349 | &mut self, |
1350 | _requester: & , |
1351 | _request: &RepositoryIssuesRequest, |
1352 | |
1353 | todo! |
1354 | |
1355 | |
1356 | |
1357 | |
1358 | |
1359 | |
1360 | pub repository: String, |
1361 | pub name: String, |
1362 | pub value: String, |
1363 | |
1364 |