Add access list to private repos
parent: tbd commit: 8c17f89
1 | use Error; |
2 | use async_trait; |
3 | |
4 | use BranchType; |
5 | use ; |
6 | |
7 | use |
8 | AccessList, Commit, DefaultBranch, Description, IssueLabel, LatestCommit, Repository, |
9 | RepositoryCommitBeforeRequest, RepositoryDiff, RepositoryDiffFile, RepositoryDiffFileChunk, |
10 | RepositoryDiffFileInfo, RepositoryDiffFileStatus, RepositoryDiffPatchRequest, |
11 | RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, |
12 | RepositoryFileInspectRequest, RepositoryIssue, RepositoryIssueLabelsRequest, |
13 | RepositoryIssuesCountRequest, RepositoryIssuesRequest, RepositoryObjectType, |
14 | RepositoryTreeEntry, RepositoryVisibility, Visibility, |
15 | ; |
16 | use ; |
17 | use ; |
18 | use ; |
19 | use Value; |
20 | use PgPool; |
21 | use |
22 | , |
23 | , | Arc
24 | ; |
25 | use Error; |
26 | use Mutex; |
27 | |
28 | use ; |
29 | |
30 | // TODO: Handle this |
31 | //region database structures |
32 | |
33 | /// Repository in the database |
34 | |
35 | |
36 | #[sqlx(try_from = "String")] |
37 | pub owner_user: User, |
38 | pub name: String, |
39 | pub description: , |
40 | pub visibility: RepositoryVisibility, |
41 | pub default_branch: String, |
42 | |
43 | |
44 | |
45 | // Separate function because "Private" will be expanded later |
46 | /// Checks if the user is allowed to view this repository |
47 | pub async |
48 | &self, |
49 | our_instance: &Instance, |
50 | user: , |
51 | settings: & , |
52 | |
53 | if matches! |
54 | return true; |
55 | |
56 | |
57 | // User must exist for any further checks to pass |
58 | let user = match user |
59 | Some => user, |
60 | None => return false, |
61 | ; |
62 | |
63 | if *user == self.owner_user |
64 | // owner can always view |
65 | return true; |
66 | |
67 | |
68 | if matches! |
69 | // Check if the user can view |
70 | let mut settings = settings.lock .await; |
71 | |
72 | let access_list = settings |
73 | .repository_get |
74 | &Repository |
75 | owner: self.owner_user.clone, |
76 | name: self.name.clone, |
77 | instance: our_instance.clone, |
78 | , |
79 | , | name
80 | |
81 | .await; |
82 | |
83 | let access_list: AccessList = match access_list |
84 | Ok => from_value .unwrap, |
85 | Err => |
86 | return false; |
87 | |
88 | ; |
89 | |
90 | access_list |
91 | .0 |
92 | .iter |
93 | .find |
94 | .is_some |
95 | else |
96 | false |
97 | |
98 | |
99 | |
100 | // This is in it's own function because I assume I'll have to add logic to this later |
101 | |
102 | &self, |
103 | repository_directory: &str, |
104 | |
105 | match open |
106 | "{}/{}/{}/{}" |
107 | repository_directory, self.owner_user.instance.url, self.owner_user.username, self.name |
108 | ) |
109 | Ok => Ok, |
110 | Err => |
111 | let err = FailedOpeningFromDisk; |
112 | error!; |
113 | |
114 | Err |
115 | |
116 | |
117 | |
118 | |
119 | |
120 | //endregion |
121 | |
122 | |
123 | |
124 | |
125 | FailedCreatingRepository, |
126 | |
127 | FailedInsertingIntoDatabase, |
128 | |
129 | RepositoryNotFound , |
130 | |
131 | RepositoryAlreadyExists , |
132 | |
133 | CouldNotDeleteFromDisk, |
134 | |
135 | FailedDeletingFromDatabase, |
136 | |
137 | FailedOpeningFromDisk, |
138 | |
139 | RefNotFound, |
140 | |
141 | PathNotFound, |
142 | |
143 | LastCommitNotFound, |
144 | |
145 | InvalidObjectId, |
146 | |
147 | BlobNotFound, |
148 | |
149 | TreeNotFound, |
150 | |
151 | CommitNotFound, |
152 | |
153 | CommitParentNotFound, |
154 | |
155 | FailedDiffing, |
156 | |
157 | |
158 | |
159 | pub pg_pool: PgPool, |
160 | pub repository_folder: String, |
161 | pub instance: Instance, |
162 | pub settings_provider: , |
163 | |
164 | |
165 | |
166 | |
167 | pg_pool: &PgPool, |
168 | repository_folder: &str, |
169 | instance: impl , |
170 | settings_provider: , |
171 | |
172 | Self |
173 | pg_pool: pg_pool.clone, |
174 | repository_folder: repository_folder.to_string, |
175 | instance: instance.to_owned, |
176 | settings_provider, |
177 | |
178 | |
179 | |
180 | pub async |
181 | &self, |
182 | user: &User, |
183 | repository_name: &str, |
184 | |
185 | if let Ok = query_as! |
186 | r#"SELECT owner_user, name, description, visibility as "visibility: _", default_branch FROM repositories WHERE owner_user = $1 AND name = $2"#, |
187 | user.to_string, repository_name |
188 | .fetch_one |
189 | .await |
190 | Ok |
191 | else |
192 | Err |
193 | owner_user: user.to_string, |
194 | name: repository_name.to_string, |
195 | |
196 | |
197 | |
198 | |
199 | pub async |
200 | &self, |
201 | user: &User, |
202 | repository_name: &str, |
203 | |
204 | if let Err = remove_dir_all |
205 | "{}/{}/{}/{}" |
206 | self.repository_folder, user.instance.url, user.username, repository_name |
207 | ) |
208 | let err = CouldNotDeleteFromDisk; |
209 | error! |
210 | "Couldn't delete repository from disk, this is bad! {:?}", |
211 | err |
212 | ; |
213 | |
214 | return Err; |
215 | |
216 | |
217 | // Delete the repository from the database |
218 | match query! |
219 | "DELETE FROM repositories WHERE owner_user = $1 AND name = $2", |
220 | user.to_string, |
221 | repository_name |
222 | |
223 | .execute |
224 | .await |
225 | |
226 | Ok => Ok, |
227 | Err => Err, |
228 | |
229 | |
230 | |
231 | pub async |
232 | &self, |
233 | owner: &User, |
234 | name: &str, |
235 | requester: , |
236 | |
237 | let repository = match self |
238 | .find_by_owner_user_name |
239 | // &request.owner.instance.url, |
240 | owner, name, |
241 | |
242 | .await |
243 | |
244 | Ok => repository, |
245 | Err => return Err, |
246 | ; |
247 | |
248 | if let Some = requester |
249 | if !repository |
250 | .can_user_view_repository |
251 | .await |
252 | |
253 | return Err |
254 | owner_user: repository.owner_user.to_string, |
255 | name: repository.name.clone, |
256 | ; |
257 | |
258 | else if matches! |
259 | // Unauthenticated users can never view private repositories |
260 | |
261 | return Err |
262 | owner_user: repository.owner_user.to_string, |
263 | name: repository.name.clone, |
264 | ; |
265 | |
266 | |
267 | match repository.open_git2_repository |
268 | Ok => Ok, |
269 | Err => return Err, |
270 | |
271 | |
272 | |
273 | // TODO: Find where this fits |
274 | // TODO: Cache this and general repository tree and invalidate select files on push |
275 | // TODO: Find better and faster technique for this |
276 | |
277 | path: &str, |
278 | git: & Repository, |
279 | start_commit: & Commit, |
280 | |
281 | let mut revwalk = git.revwalk?; |
282 | revwalk.set_sorting?; |
283 | revwalk.push?; |
284 | |
285 | for oid in revwalk |
286 | let oid = oid?; |
287 | let commit = git.find_commit?; |
288 | |
289 | // Merge commits have 2 or more parents |
290 | // Commits with 0 parents are handled different because we can't diff against them |
291 | if commit.parent_count == 0 |
292 | return Ok; |
293 | else if commit.parent_count == 1 |
294 | let tree = commit.tree?; |
295 | let last_tree = commit.parent?.tree?; |
296 | |
297 | // Get the diff between the current tree and the last one |
298 | let diff = git.diff_tree_to_tree?; |
299 | |
300 | for dd in diff.deltas |
301 | // Get the path of the current file we're diffing against |
302 | let current_path = dd.new_file .path .unwrap; |
303 | |
304 | // Path or directory |
305 | if current_path.eq || current_path.starts_with |
306 | return Ok; |
307 | |
308 | |
309 | |
310 | |
311 | |
312 | Err? |
313 | |
314 | |
315 | |
316 | |
317 | |
318 | async |
319 | if let Ok = self |
320 | .find_by_owner_user_name |
321 | .await |
322 | |
323 | Ok |
324 | else |
325 | Ok |
326 | |
327 | |
328 | |
329 | async |
330 | &mut self, |
331 | _user: &User, |
332 | request: &RepositoryCreateRequest, |
333 | |
334 | // Check if repository already exists in the database |
335 | if let Ok = self |
336 | .find_by_owner_user_name |
337 | .await |
338 | |
339 | let err = RepositoryAlreadyExists |
340 | owner_user: repository.owner_user.to_string, |
341 | name: repository.name, |
342 | ; |
343 | error!; |
344 | |
345 | return Err; |
346 | |
347 | |
348 | // Insert the repository into the database |
349 | let _ = match query_as! |
350 | r#"INSERT INTO repositories VALUES ($1, $2, $3, $4, $5) RETURNING owner_user, name, description, visibility as "visibility: _", default_branch"#, |
351 | request.owner.to_string, request.name, request.description, request.visibility as _, "master" |
352 | .fetch_one |
353 | .await |
354 | Ok => repository, |
355 | Err => |
356 | let err = FailedInsertingIntoDatabase; |
357 | error!; |
358 | |
359 | return Err; |
360 | |
361 | ; |
362 | |
363 | // Create bare (server side) repository on disk |
364 | match init_bare |
365 | "{}/{}/{}/{}" |
366 | self.repository_folder, |
367 | request.owner.instance.url, |
368 | request.owner.username, |
369 | request.name |
370 | ) |
371 | Ok => |
372 | debug! |
373 | "Created new repository with the name {}/{}/{}", |
374 | request.owner.instance.url, request.owner.username, request.name |
375 | ; |
376 | |
377 | let repository = Repository |
378 | owner: request.owner.clone, |
379 | name: request.name.clone, |
380 | instance: request.instance.as_ref .unwrap_or .clone, |
381 | ; |
382 | |
383 | let mut settings_backend = self.settings_provider.lock .await; |
384 | settings_backend |
385 | .repository_write |
386 | &repository, |
387 | , | name
388 | AnySetting |
389 | to_value |
390 | request.description.clone .unwrap_or_default, |
391 | |
392 | .unwrap, |
393 | , |
394 | |
395 | .await |
396 | .unwrap; |
397 | settings_backend |
398 | .repository_write |
399 | &repository, |
400 | , | name
401 | AnySetting |
402 | unwrap, | to_value .
403 | , |
404 | |
405 | .await |
406 | .unwrap; |
407 | settings_backend |
408 | .repository_write |
409 | &repository, |
410 | , | name
411 | AnySetting |
412 | to_value |
413 | .unwrap, |
414 | , |
415 | |
416 | .await |
417 | .unwrap; |
418 | |
419 | Ok |
420 | |
421 | Err => |
422 | let err = FailedCreatingRepository; |
423 | error!; |
424 | |
425 | // Delete repository from database |
426 | self.delete_by_owner_user_name |
427 | .await?; |
428 | |
429 | // ??? |
430 | Err |
431 | |
432 | |
433 | |
434 | |
435 | async |
436 | &mut self, |
437 | repository: &Repository, |
438 | name: &str, |
439 | |
440 | Ok |
441 | if name == value_name |
442 | from_raw |
443 | else if name == value_name |
444 | from_raw |
445 | else if name == value_name |
446 | from_raw |
447 | else if name == value_name |
448 | from_raw |
449 | else |
450 | return Err; |
451 | |
452 | |
453 | |
454 | |
455 | async |
456 | &mut self, |
457 | repository: &Repository, |
458 | name: &str, |
459 | |
460 | let mut provider = self.settings_provider.lock .await; |
461 | |
462 | Ok |
463 | |
464 | |
465 | async |
466 | &mut self, |
467 | repository: &Repository, |
468 | name: &str, |
469 | setting: &Value, |
470 | |
471 | let mut provider = self.settings_provider.lock .await; |
472 | |
473 | provider |
474 | .repository_write |
475 | .await |
476 | |
477 | |
478 | // async fn repository_info( |
479 | // &mut self, |
480 | // requester: Option<&User>, |
481 | // request: &RepositoryInfoRequest, |
482 | // ) -> Result<RepositoryView, Error> { |
483 | // let repository = match self |
484 | // .find_by_owner_user_name( |
485 | // // &request.owner.instance.url, |
486 | // &request.repository.owner, |
487 | // &request.repository.name, |
488 | // ) |
489 | // .await |
490 | // { |
491 | // Ok(repository) => repository, |
492 | // Err(err) => return Err(Box::new(err).into()), |
493 | // }; |
494 | |
495 | // if let Some(requester) = requester { |
496 | // if !repository.can_user_view_repository(Some(&requester)) { |
497 | // return Err(Box::new(GitBackendError::RepositoryNotFound { |
498 | // owner_user: request.repository.owner.to_string(), |
499 | // name: request.repository.name.clone(), |
500 | // }) |
501 | // .into()); |
502 | // } |
503 | // } else if matches!(repository.visibility, RepositoryVisibility::Private) { |
504 | // // Unauthenticated users can never view private repositories |
505 | |
506 | // return Err(Box::new(GitBackendError::RepositoryNotFound { |
507 | // owner_user: request.repository.owner.to_string(), |
508 | // name: request.repository.name.clone(), |
509 | // }) |
510 | // .into()); |
511 | // } |
512 | |
513 | // let git = match repository.open_git2_repository(&self.repository_folder) { |
514 | // Ok(git) => git, |
515 | // Err(err) => return Err(Box::new(err).into()), |
516 | // }; |
517 | |
518 | // let rev_name = match &request.rev { |
519 | // None => { |
520 | // if let Ok(head) = git.head() { |
521 | // head.name().unwrap().to_string() |
522 | // } else { |
523 | // // Nothing in database, render empty tree. |
524 | // return Ok(RepositoryView { |
525 | // name: repository.name, |
526 | // owner: request.repository.owner.clone(), |
527 | // description: repository.description, |
528 | // visibility: repository.visibility, |
529 | // default_branch: repository.default_branch, |
530 | // latest_commit: None, |
531 | // tree_rev: None, |
532 | // tree: vec![], |
533 | // }); |
534 | // } |
535 | // } |
536 | // Some(rev_name) => { |
537 | // // Find the reference, otherwise return GitBackendError |
538 | // match git |
539 | // .find_reference(format!("refs/heads/{}", rev_name).as_str()) |
540 | // .map_err(|_| GitBackendError::RefNotFound(rev_name.to_string())) |
541 | // { |
542 | // Ok(reference) => reference.name().unwrap().to_string(), |
543 | // Err(err) => return Err(Box::new(err).into()), |
544 | // } |
545 | // } |
546 | // }; |
547 | |
548 | // // Get the git object as a commit |
549 | // let rev = match git |
550 | // .revparse_single(rev_name.as_str()) |
551 | // .map_err(|_| GitBackendError::RefNotFound(rev_name.to_string())) |
552 | // { |
553 | // Ok(rev) => rev, |
554 | // Err(err) => return Err(Box::new(err).into()), |
555 | // }; |
556 | // let commit = rev.as_commit().unwrap(); |
557 | |
558 | // // this is stupid |
559 | // let mut current_path = rev_name.replace("refs/heads/", ""); |
560 | |
561 | // // Get the commit tree |
562 | // let git_tree = if let Some(path) = &request.path { |
563 | // // Add it to our full path string |
564 | // current_path.push_str(format!("/{}", path).as_str()); |
565 | // // Get the specified path, return an error if it wasn't found. |
566 | // let entry = match commit |
567 | // .tree() |
568 | // .unwrap() |
569 | // .get_path(&PathBuf::from(path)) |
570 | // .map_err(|_| GitBackendError::PathNotFound(path.to_string())) |
571 | // { |
572 | // Ok(entry) => entry, |
573 | // Err(err) => return Err(Box::new(err).into()), |
574 | // }; |
575 | // // Turn the entry into a git tree |
576 | // entry.to_object(&git).unwrap().as_tree().unwrap().clone() |
577 | // } else { |
578 | // commit.tree().unwrap() |
579 | // }; |
580 | |
581 | // // Iterate over the git tree and collect it into our own tree types |
582 | // let mut tree = git_tree |
583 | // .iter() |
584 | // .map(|entry| { |
585 | // let object_type = match entry.kind().unwrap() { |
586 | // ObjectType::Tree => RepositoryObjectType::Tree, |
587 | // ObjectType::Blob => RepositoryObjectType::Blob, |
588 | // _ => unreachable!(), |
589 | // }; |
590 | // let mut tree_entry = |
591 | // RepositoryTreeEntry::new(entry.name().unwrap(), object_type, entry.filemode()); |
592 | |
593 | // if request.extra_metadata { |
594 | // // Get the file size if It's a blob |
595 | // let object = entry.to_object(&git).unwrap(); |
596 | // if let Some(blob) = object.as_blob() { |
597 | // tree_entry.size = Some(blob.size()); |
598 | // } |
599 | |
600 | // // Could possibly be done better |
601 | // let path = if let Some(path) = current_path.split_once('/') { |
602 | // format!("{}/{}", path.1, entry.name().unwrap()) |
603 | // } else { |
604 | // entry.name().unwrap().to_string() |
605 | // }; |
606 | |
607 | // // Get the last commit made to the entry |
608 | // if let Ok(last_commit) = |
609 | // GitBackend::get_last_commit_of_file(&path, &git, commit) |
610 | // { |
611 | // tree_entry.last_commit = Some(last_commit); |
612 | // } |
613 | // } |
614 | |
615 | // tree_entry |
616 | // }) |
617 | // .collect::<Vec<RepositoryTreeEntry>>(); |
618 | |
619 | // // Sort the tree alphabetically and with tree first |
620 | // tree.sort_unstable_by_key(|entry| entry.name.to_lowercase()); |
621 | // tree.sort_unstable_by_key(|entry| { |
622 | // std::cmp::Reverse(format!("{:?}", entry.object_type).to_lowercase()) |
623 | // }); |
624 | |
625 | // Ok(RepositoryView { |
626 | // name: repository.name, |
627 | // owner: request.repository.owner.clone(), |
628 | // description: repository.description, |
629 | // visibility: repository.visibility, |
630 | // default_branch: repository.default_branch, |
631 | // latest_commit: Some(Commit::from(commit.clone())), |
632 | // tree_rev: Some(rev_name), |
633 | // tree, |
634 | // }) |
635 | // } |
636 | |
637 | async |
638 | &mut self, |
639 | requester: , |
640 | repository: &Repository, |
641 | request: &RepositoryFileInspectRequest, |
642 | |
643 | let git = self |
644 | .open_repository_and_check_permissions |
645 | .await?; |
646 | |
647 | // Try and parse the input as a reference and get the object ID |
648 | let mut tree_id = match &request.rev |
649 | None => |
650 | if let Ok = git.head |
651 | // TODO: Fix for symbolic references |
652 | head.target |
653 | else |
654 | // Nothing in database, render empty tree. |
655 | return Ok; |
656 | |
657 | |
658 | Some => |
659 | // Find the reference, otherwise return GitBackendError |
660 | git.refname_to_id .ok |
661 | |
662 | ; |
663 | |
664 | // If the reference wasn't found, try parsing it as a commit ID |
665 | if tree_id.is_none |
666 | if let Ok = from_str |
667 | tree_id = Some |
668 | |
669 | |
670 | |
671 | // If the commit ID wasn't found, try parsing it as a branch and otherwise return error |
672 | if tree_id.is_none |
673 | match git.find_branch |
674 | Ok => tree_id = branch.get .target, |
675 | Err => |
676 | return Err |
677 | request.rev.clone .unwrap, |
678 | |
679 | .into |
680 | |
681 | |
682 | |
683 | |
684 | // unwrap might be dangerous? |
685 | // Get the commit from the oid |
686 | let commit = git.find_commit .unwrap; |
687 | |
688 | // this is stupid |
689 | let mut current_path = request.rev.clone .unwrap_or_else; |
690 | |
691 | // Get the commit tree |
692 | let git_tree = if let Some = &request.path |
693 | // Add it to our full path string |
694 | current_path.push_str; |
695 | // Get the specified path, return an error if it wasn't found. |
696 | let entry = match commit |
697 | .tree |
698 | .unwrap |
699 | .get_path |
700 | .map_err |
701 | |
702 | Ok => entry, |
703 | Err => return Err, |
704 | ; |
705 | // Turn the entry into a git tree |
706 | entry.to_object .unwrap .as_tree .unwrap .clone |
707 | else |
708 | commit.tree .unwrap |
709 | ; |
710 | |
711 | // Iterate over the git tree and collect it into our own tree types |
712 | let mut tree = git_tree |
713 | .iter |
714 | .map |
715 | let object_type = match entry.kind .unwrap |
716 | => Tree, | Tree
717 | => Blob, | Blob
718 | _ => unreachable!, |
719 | ; |
720 | let mut tree_entry = new |
721 | entry.id .to_string .as_str, |
722 | entry.name .unwrap, |
723 | object_type, |
724 | entry.filemode, |
725 | ; |
726 | |
727 | if request.extra_metadata |
728 | // Get the file size if It's a blob |
729 | let object = entry.to_object .unwrap; |
730 | if let Some = object.as_blob |
731 | tree_entry.size = Some; |
732 | |
733 | |
734 | // Could possibly be done better |
735 | let path = if let Some = current_path.split_once |
736 | format! |
737 | else |
738 | entry.name .unwrap .to_string |
739 | ; |
740 | |
741 | // Get the last commit made to the entry |
742 | if let Ok = |
743 | get_last_commit_of_file |
744 | |
745 | tree_entry.last_commit = Some; |
746 | |
747 | |
748 | |
749 | tree_entry |
750 | |
751 | .; |
752 | |
753 | // Sort the tree alphabetically and with tree first |
754 | tree.sort_unstable_by_key; |
755 | tree.sort_unstable_by_key |
756 | Reverse |
757 | ; |
758 | |
759 | Ok |
760 | |
761 | |
762 | async |
763 | &mut self, |
764 | requester: , |
765 | repository: &Repository, |
766 | request: &RepositoryFileFromIdRequest, |
767 | |
768 | let git = self |
769 | .open_repository_and_check_permissions |
770 | .await?; |
771 | |
772 | // Parse the passed object id |
773 | let oid = match from_str |
774 | Ok => oid, |
775 | Err => |
776 | return Err |
777 | |
778 | ; |
779 | |
780 | // Find the file and turn it into our own struct |
781 | let file = match git.find_blob |
782 | Ok => RepositoryFile |
783 | content: blob.content .to_vec, |
784 | binary: blob.is_binary, |
785 | size: blob.size, |
786 | , |
787 | Err => return Err, |
788 | ; |
789 | |
790 | Ok |
791 | |
792 | |
793 | async |
794 | &mut self, |
795 | requester: , |
796 | repository: &Repository, |
797 | request: &RepositoryDiffRequest, |
798 | |
799 | let git = self |
800 | .open_repository_and_check_permissions |
801 | .await?; |
802 | |
803 | // Parse the passed object ids |
804 | let oid_old = from_str |
805 | .map_err?; |
806 | let oid_new = from_str |
807 | .map_err?; |
808 | |
809 | // Get the ids associates commits |
810 | let commit_old = git |
811 | .find_commit |
812 | .map_err?; |
813 | let commit_new = git |
814 | .find_commit |
815 | .map_err?; |
816 | |
817 | // Get the commit trees |
818 | let tree_old = commit_old |
819 | .tree |
820 | .map_err?; |
821 | let tree_new = commit_new |
822 | .tree |
823 | .map_err?; |
824 | |
825 | // Diff the two trees against each other |
826 | let diff = git |
827 | .diff_tree_to_tree |
828 | .map_err |
829 | FailedDiffing |
830 | ?; |
831 | |
832 | // Should be safe to unwrap? |
833 | let stats = diff.stats .unwrap; |
834 | let mut files: = vec!; |
835 | |
836 | diff.deltas .enumerate .for_each |
837 | // Parse the old file info from the delta |
838 | let old_file_info = match delta.old_file .exists |
839 | true => Some |
840 | id: delta.old_file .id .to_string, |
841 | path: delta |
842 | .old_file |
843 | .path |
844 | .unwrap |
845 | .to_str |
846 | .unwrap |
847 | .to_string, |
848 | size: delta.old_file .size, |
849 | binary: delta.old_file .is_binary, |
850 | , |
851 | false => None, |
852 | ; |
853 | // Parse the new file info from the delta |
854 | let new_file_info = match delta.new_file .exists |
855 | true => Some |
856 | id: delta.new_file .id .to_string, |
857 | path: delta |
858 | .new_file |
859 | .path |
860 | .unwrap |
861 | .to_str |
862 | .unwrap |
863 | .to_string, |
864 | size: delta.new_file .size, |
865 | binary: delta.new_file .is_binary, |
866 | , |
867 | false => None, |
868 | ; |
869 | |
870 | let mut chunks: = vec!; |
871 | if let Some = from_diff .ok .flatten |
872 | for chunk_num in 0..patch.num_hunks |
873 | if let Ok = patch.hunk |
874 | let mut lines: = vec!; |
875 | |
876 | for line_num in 0..chunk_num_lines |
877 | if let Ok = patch.line_in_hunk |
878 | if let Ok = String from_utf8 |
879 | lines.push; |
880 | |
881 | |
882 | continue; |
883 | |
884 | |
885 | lines.push; |
886 | |
887 | |
888 | chunks.push |
889 | header: String from_utf8 .ok, |
890 | old_start: chunk.old_start, |
891 | old_lines: chunk.old_lines, |
892 | new_start: chunk.new_start, |
893 | new_lines: chunk.new_lines, |
894 | lines, |
895 | ; |
896 | |
897 | |
898 | ; |
899 | |
900 | let file = RepositoryDiffFile |
901 | status: from, |
902 | old_file_info, |
903 | new_file_info, |
904 | chunks, |
905 | ; |
906 | |
907 | files.push; |
908 | ; |
909 | |
910 | Ok |
911 | new_commit: from, |
912 | files_changed: stats.files_changed, |
913 | insertions: stats.insertions, |
914 | deletions: stats.deletions, |
915 | files, |
916 | |
917 | |
918 | |
919 | async |
920 | &mut self, |
921 | requester: , |
922 | repository: &Repository, |
923 | request: &RepositoryDiffPatchRequest, |
924 | |
925 | let git = self |
926 | .open_repository_and_check_permissions |
927 | .await?; |
928 | |
929 | // Parse the passed object ids |
930 | let oid_old = from_str |
931 | .map_err?; |
932 | let oid_new = from_str |
933 | .map_err?; |
934 | |
935 | // Get the ids associates commits |
936 | let commit_old = git |
937 | .find_commit |
938 | .map_err?; |
939 | let commit_new = git |
940 | .find_commit |
941 | .map_err?; |
942 | |
943 | // Get the commit trees |
944 | let tree_old = commit_old |
945 | .tree |
946 | .map_err?; |
947 | let tree_new = commit_new |
948 | .tree |
949 | .map_err?; |
950 | |
951 | // Diff the two trees against each other |
952 | let diff = git |
953 | .diff_tree_to_tree |
954 | .map_err |
955 | FailedDiffing |
956 | ?; |
957 | |
958 | // Print the entire patch |
959 | let mut patch = String new; |
960 | |
961 | diff.print |
962 | match line.origin |
963 | '+' | '-' | ' ' => patch.push, |
964 | _ => |
965 | |
966 | patch.push_str; |
967 | true |
968 | |
969 | .unwrap; |
970 | |
971 | Ok |
972 | |
973 | |
974 | async |
975 | &mut self, |
976 | requester: , |
977 | repository: &Repository, |
978 | request: &RepositoryCommitBeforeRequest, |
979 | |
980 | let git = self |
981 | .open_repository_and_check_permissions |
982 | .await?; |
983 | |
984 | // Parse the passed object id |
985 | let oid = match from_str |
986 | Ok => oid, |
987 | Err => |
988 | return Err |
989 | |
990 | ; |
991 | |
992 | // Find the commit using the parsed oid |
993 | let commit = match git.find_commit |
994 | Ok => commit, |
995 | Err => return Err, |
996 | ; |
997 | |
998 | // Get the first parent it has |
999 | let parent = match commit.parent |
1000 | Ok => from, |
1001 | Err => |
1002 | return Err |
1003 | |
1004 | ; |
1005 | |
1006 | Ok |
1007 | |
1008 | |
1009 | |
1010 | |
1011 | |
1012 | &mut self, |
1013 | _requester: , |
1014 | _request: &RepositoryIssuesCountRequest, |
1015 | |
1016 | todo! |
1017 | |
1018 | |
1019 | |
1020 | &mut self, |
1021 | _requester: , |
1022 | _request: &RepositoryIssueLabelsRequest, |
1023 | |
1024 | todo! |
1025 | |
1026 | |
1027 | |
1028 | &mut self, |
1029 | _requester: , |
1030 | _request: &RepositoryIssuesRequest, |
1031 | |
1032 | todo! |
1033 | |
1034 | |
1035 | |
1036 | |
1037 | |
1038 | |
1039 | pub repository: String, |
1040 | pub name: String, |
1041 | pub value: String, |
1042 | |
1043 |