use anyhow::Error; use giterated_models::{ object::Object, repository::{ Commit, CommitSignature, Repository, RepositoryTag, RepositoryTagRequest, RepositoryTagsRequest, }, }; use giterated_stack::{AuthenticatedUser, GiteratedStack, OperationState, StackOperationState}; use super::{parse_trim_git_message, GitBackend, GitBackendError}; impl GitBackend { /// Converts a git2 tag into our own type, couldn't implement [`From`] or impl on [`RepositoryTag`] from here. pub fn git2_annotated_tag_to_own_tag( id: String, name: String, tag: git2::Tag, ) -> RepositoryTag { // Get the tag message and split it into a summary and body let (summary, body) = if let Some(message) = tag.message() { parse_trim_git_message(message) } else { (None, None) }; // Get the commit the tag is (possibly) pointing to let commit = tag .peel() .map(|obj| obj.into_commit().ok()) .ok() .flatten() .map(|c| Commit::from(c)); // Get the author of the tag let author: Option = tag.tagger().map(|s| s.into()); // Get the time the tag or pointed commit was created let time = if let Some(ref author) = author { Some(author.time) } else { // Get possible commit time if the tag has no author time commit.as_ref().map(|c| c.time.clone()) }; RepositoryTag { id: id.to_string(), name: name.to_string(), summary, body, author, time, commit, } } /// .0: List of tags in passed range /// .1: Total amount of tags pub async fn handle_repository_get_tags( &mut self, requester: &Option, repository_object: &mut Object<'_, StackOperationState, Repository, GiteratedStack>, OperationState(_operation_state): OperationState, request: &RepositoryTagsRequest, ) -> Result<(Vec, usize), Error> { let repository = repository_object.object(); let git = self .open_repository_and_check_permissions(&repository.owner, &repository.name, requester) .await?; let mut tags = vec![]; // Iterate over each tag let _ = git.tag_foreach(|id, name| { // Get the name in utf8 let name = String::from_utf8_lossy(name).replacen("refs/tags/", "", 1); // Find the tag so we can get the messages attached if any if let Ok(tag) = git.find_tag(id) { tags.push(Self::git2_annotated_tag_to_own_tag( id.to_string(), name, tag, )); } else { // Lightweight commit, we try and find the commit it's pointing to let commit = git.find_commit(id).ok().map(|c| Commit::from(c)); tags.push(RepositoryTag { id: id.to_string(), name: name.to_string(), summary: None, body: None, author: None, time: commit.as_ref().map(|c| c.time.clone()), commit, }); }; true }); // Get the total amount of tags let tag_count = tags.len(); if let Some(search) = &request.search { // TODO: Caching // Search by sorting using a simple fuzzy search algorithm tags.sort_by(|n1, n2| { strsim::damerau_levenshtein(search, &n1.name) .cmp(&strsim::damerau_levenshtein(search, &n2.name)) }); } else { // Sort the tags using their creation or pointer date tags.sort_by(|t1, t2| t2.time.cmp(&t1.time)); } // Get the requested range of tags let tags = tags .into_iter() .skip(request.range.0) .take(request.range.1.saturating_sub(request.range.0)) .collect::>(); Ok((tags, tag_count)) } pub async fn handle_repository_get_tag( &mut self, requester: &Option, repository_object: &mut Object<'_, StackOperationState, Repository, GiteratedStack>, OperationState(_operation_state): OperationState, request: &RepositoryTagRequest, ) -> Result { let repository = repository_object.object(); let git = self .open_repository_and_check_permissions(&repository.owner, &repository.name, requester) .await?; // Get the tag id by parsing the ref let full_ref_name = format!("refs/tags/{}", request.name.clone()); let tag_id = git .refname_to_id(&full_ref_name) .map_err(|_| GitBackendError::RefNotFound(full_ref_name.clone()))?; let tag = git.find_tag(tag_id); if let Ok(tag) = tag { // Convert the annotated tag into our own type Ok(Self::git2_annotated_tag_to_own_tag( tag_id.to_string(), request.name.clone(), tag, )) } else { // Lightweight tag, we try and find the commit it's pointing to let commit = git.find_commit(tag_id).ok().map(|c| Commit::from(c)); Ok(RepositoryTag { id: tag_id.to_string(), name: request.name.clone(), summary: None, body: None, author: None, time: commit.as_ref().map(|c| c.time.clone()), commit, }) } } }