use anyhow::Error; use giterated_models::{ object::Object, repository::{Commit, CommitSignature, Repository, RepositoryTag, RepositoryTagsRequest}, }; use giterated_stack::{AuthenticatedUser, GiteratedStack, OperationState, StackOperationState}; use super::GitBackend; impl GitBackend { /// .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) { // Get the tag message and split it into a summary and body let (summary, body) = if let Some(message) = tag.message() { // Iterate over the lines let mut lines = message .lines() .map(|line| { // Trim the whitespace for every line let mut whitespace_removed = String::with_capacity(line.len()); line.split_whitespace().for_each(|word| { if !whitespace_removed.is_empty() { whitespace_removed.push(' '); } whitespace_removed.push_str(word); }); whitespace_removed }) .collect::>(); let summary = Some(lines.remove(0)); let body = if lines.is_empty() { None } else { Some(lines.join("\n")) }; (summary, body) } 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()) }; tags.push(RepositoryTag { id: id.to_string(), name: name.to_string(), summary, body, author, time, commit, }); } 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)) } }