1use time::OffsetDateTime;
4
5use crate::{error::Error, util::convert_git2_time, GitRepository};
6
7#[derive(Debug, Clone)]
9pub struct Tag {
10 pub id: String,
12 pub date: OffsetDateTime,
14 pub name: String,
16 pub name_full: String,
18 pub message: Option<String>,
20 pub commit_id: String,
22}
23
24impl PartialEq for Tag {
25 fn eq(&self, other: &Self) -> bool {
26 self.id == other.id
27 }
28}
29
30impl Tag {
31 pub fn is_annotated(&self) -> bool {
33 self.message.is_some()
34 }
35}
36
37pub fn list_tags(repo: &GitRepository) -> Result<Vec<String>, Error> {
39 let tags = repo.tag_names(None)?;
40 let tags = tags
41 .into_iter()
42 .filter_map(|t| t.map(|s| s.to_string()))
43 .collect();
44 Ok(tags)
45}
46
47pub fn get_tag_refs(repo: &GitRepository) -> Result<Vec<Tag>, Error> {
51 let refs = repo.references()?;
52
53 let mut tags = vec![];
54 for res in refs {
55 let rf = res?;
56
57 let rf = rf.resolve()?;
59
60 let id = rf.target().unwrap().to_string();
62 let full_name = rf.name().unwrap_or("__invalid__").to_string();
63 let name = rf.shorthand().unwrap_or("__invalid__").to_string();
64
65 if !full_name.starts_with("refs/tags/") {
69 continue;
71 }
72 let tag = rf.peel_to_tag().ok();
75 let tag_message = tag.map(|t| t.message().unwrap_or("__invalid__").trim().to_string());
76
77 let commit = rf.peel_to_commit()?;
80 let commit_id = commit.id().to_string();
81 let date = convert_git2_time(commit.time())?;
82
83 tags.push({
84 Tag {
85 id,
86 date,
87 name,
88 name_full: full_name,
89 message: tag_message,
90 commit_id,
91 }
92 })
93 }
94
95 Ok(tags)
96}
97
98pub fn set_annotated_tag(repo: &GitRepository, tag: &str, message: &str) -> Result<(), Error> {
100 let head_commit = repo.head()?.peel_to_commit()?;
101 let tagger = repo.signature()?;
102 let _oid = repo.tag(tag, head_commit.as_object(), &tagger, message, false)?;
103 Ok(())
104}
105
106#[cfg(test)]
107mod tests {
108 use crate::repo::discover_repo;
109
110 use super::*;
111
112 #[test]
113 fn test_tags_simple() {
114 let cwd = std::env::current_dir().unwrap();
115 let repo = discover_repo(&cwd).unwrap();
116 let tags = list_tags(&repo).unwrap();
117 for tag in tags {
118 eprintln!("{tag}")
119 }
120 }
121
122 #[test]
123 fn test_tags_refs() {
124 let cwd = std::env::current_dir().unwrap();
125 let repo = discover_repo(&cwd).unwrap();
126 let tags = get_tag_refs(&repo).unwrap();
127 for tag in tags {
128 eprintln!("{}: {} ({})", tag.id, tag.name, tag.commit_id)
129 }
130 }
131}