JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Singular tag request

Emilia - ⁨1⁩ year ago

parent: tbd commit: ⁨d239950

⁨giterated-daemon/src/backend/git/tags.rs⁩ - ⁨5920⁩ bytes
Raw
1 use anyhow::Error;
2 use giterated_models::{
3 object::Object,
4 repository::{
5 Commit, CommitSignature, Repository, RepositoryTag, RepositoryTagRequest,
6 RepositoryTagsRequest,
7 },
8 };
9 use giterated_stack::{AuthenticatedUser, GiteratedStack, OperationState, StackOperationState};
10
11 use super::{parse_trim_git_message, GitBackend, GitBackendError};
12
13 impl GitBackend {
14 /// Converts a git2 tag into our own type, couldn't implement [`From`] or impl on [`RepositoryTag`] from here.
15 pub fn git2_annotated_tag_to_own_tag(
16 id: String,
17 name: String,
18 tag: git2::Tag,
19 ) -> RepositoryTag {
20 // Get the tag message and split it into a summary and body
21 let (summary, body) = if let Some(message) = tag.message() {
22 parse_trim_git_message(message)
23 } else {
24 (None, None)
25 };
26
27 // Get the commit the tag is (possibly) pointing to
28 let commit = tag
29 .peel()
30 .map(|obj| obj.into_commit().ok())
31 .ok()
32 .flatten()
33 .map(|c| Commit::from(c));
34 // Get the author of the tag
35 let author: Option<CommitSignature> = tag.tagger().map(|s| s.into());
36 // Get the time the tag or pointed commit was created
37 let time = if let Some(ref author) = author {
38 Some(author.time)
39 } else {
40 // Get possible commit time if the tag has no author time
41 commit.as_ref().map(|c| c.time.clone())
42 };
43
44 RepositoryTag {
45 id: id.to_string(),
46 name: name.to_string(),
47 summary,
48 body,
49 author,
50 time,
51 commit,
52 }
53 }
54
55 /// .0: List of tags in passed range
56 /// .1: Total amount of tags
57 pub async fn handle_repository_get_tags(
58 &mut self,
59 requester: &Option<AuthenticatedUser>,
60 repository_object: &mut Object<'_, StackOperationState, Repository, GiteratedStack>,
61 OperationState(_operation_state): OperationState<StackOperationState>,
62 request: &RepositoryTagsRequest,
63 ) -> Result<(Vec<RepositoryTag>, usize), Error> {
64 let repository = repository_object.object();
65 let git = self
66 .open_repository_and_check_permissions(&repository.owner, &repository.name, requester)
67 .await?;
68
69 let mut tags = vec![];
70
71 // Iterate over each tag
72 let _ = git.tag_foreach(|id, name| {
73 // Get the name in utf8
74 let name = String::from_utf8_lossy(name).replacen("refs/tags/", "", 1);
75
76 // Find the tag so we can get the messages attached if any
77 if let Ok(tag) = git.find_tag(id) {
78 tags.push(Self::git2_annotated_tag_to_own_tag(
79 id.to_string(),
80 name,
81 tag,
82 ));
83 } else {
84 // Lightweight commit, we try and find the commit it's pointing to
85 let commit = git.find_commit(id).ok().map(|c| Commit::from(c));
86
87 tags.push(RepositoryTag {
88 id: id.to_string(),
89 name: name.to_string(),
90 summary: None,
91 body: None,
92 author: None,
93 time: commit.as_ref().map(|c| c.time.clone()),
94 commit,
95 });
96 };
97
98 true
99 });
100
101 // Get the total amount of tags
102 let tag_count = tags.len();
103
104 if let Some(search) = &request.search {
105 // TODO: Caching
106 // Search by sorting using a simple fuzzy search algorithm
107 tags.sort_by(|n1, n2| {
108 strsim::damerau_levenshtein(search, &n1.name)
109 .cmp(&strsim::damerau_levenshtein(search, &n2.name))
110 });
111 } else {
112 // Sort the tags using their creation or pointer date
113 tags.sort_by(|t1, t2| t2.time.cmp(&t1.time));
114 }
115
116 // Get the requested range of tags
117 let tags = tags
118 .into_iter()
119 .skip(request.range.0)
120 .take(request.range.1.saturating_sub(request.range.0))
121 .collect::<Vec<RepositoryTag>>();
122
123 Ok((tags, tag_count))
124 }
125
126 pub async fn handle_repository_get_tag(
127 &mut self,
128 requester: &Option<AuthenticatedUser>,
129 repository_object: &mut Object<'_, StackOperationState, Repository, GiteratedStack>,
130 OperationState(_operation_state): OperationState<StackOperationState>,
131 request: &RepositoryTagRequest,
132 ) -> Result<RepositoryTag, Error> {
133 let repository = repository_object.object();
134 let git = self
135 .open_repository_and_check_permissions(&repository.owner, &repository.name, requester)
136 .await?;
137
138 // Get the tag id by parsing the ref
139 let full_ref_name = format!("refs/tags/{}", request.name.clone());
140 let tag_id = git
141 .refname_to_id(&full_ref_name)
142 .map_err(|_| GitBackendError::RefNotFound(full_ref_name.clone()))?;
143
144 let tag = git.find_tag(tag_id);
145 if let Ok(tag) = tag {
146 // Convert the annotated tag into our own type
147 Ok(Self::git2_annotated_tag_to_own_tag(
148 tag_id.to_string(),
149 request.name.clone(),
150 tag,
151 ))
152 } else {
153 // Lightweight tag, we try and find the commit it's pointing to
154 let commit = git.find_commit(tag_id).ok().map(|c| Commit::from(c));
155
156 Ok(RepositoryTag {
157 id: tag_id.to_string(),
158 name: request.name.clone(),
159 summary: None,
160 body: None,
161 author: None,
162 time: commit.as_ref().map(|c| c.time.clone()),
163 commit,
164 })
165 }
166 }
167 }
168