1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use serde::{Deserialize, Serialize};
use std::fmt::{self, Debug, Display, Formatter};

#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct TagInfo<'a> {
    /// The name of the tag
    pub tag: &'a str,

    /// The number of commits since the tag
    pub commits_since_tag: u32,
}

#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(bound(deserialize = "'de: 'a"))]
pub struct GitInfo<'a, 'b> {
    /// Information on the tag that is the closest ancestor tag to the current commit.
    /// tag_info can be `None` if the repository doesn't have any tags or is a shallow clone and tags aren't available
    pub tag_info: Option<TagInfo<'a>>,

    /// ID of the current commit
    pub commit_id: &'b str,

    /// Whether the working directory was modified or whether the build was done from a clean working directory
    pub modified: bool,
}

impl<'a, 'b> Debug for GitInfo<'a, 'b> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        Display::fmt(self, f)
    }
}

impl<'a, 'b> Display for GitInfo<'a, 'b> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        if let Some(tag) = self.tag_info {
            write!(f, "{}+{}", tag.tag, tag.commits_since_tag)?;
        } else {
            write!(f, "unknown")?;
        }
        write!(f, ".g{}", self.commit_id)?;
        if self.modified {
            write!(f, ".modified")?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    mod display {
        use super::*;

        #[test]
        fn notag_notmodified() {
            let version = GitInfo {
                tag_info: None,
                commit_id: "abcdef",
                modified: false,
            };
            assert_eq!("unknown.gabcdef", format!("{}", version));
            assert_eq!("unknown.gabcdef", format!("{:?}", version));
        }

        #[test]
        fn notag_modified() {
            let version = GitInfo {
                tag_info: None,
                commit_id: "abcdef",
                modified: true,
            };
            assert_eq!("unknown.gabcdef.modified", format!("{}", version));
            assert_eq!("unknown.gabcdef.modified", format!("{:?}", version));
        }

        #[test]
        fn notontag_notmodified() {
            let version = GitInfo {
                tag_info: Some(TagInfo {
                    tag: "v1.2.3",
                    commits_since_tag: 10,
                }),
                commit_id: "abcdef",
                modified: false,
            };
            assert_eq!("v1.2.3+10.gabcdef", format!("{}", version));
            assert_eq!("v1.2.3+10.gabcdef", format!("{:?}", version));
        }

        #[test]
        fn notontag_modified() {
            let version = GitInfo {
                tag_info: Some(TagInfo {
                    tag: "v1.2.3",
                    commits_since_tag: 10,
                }),
                commit_id: "abcdef",
                modified: true,
            };
            assert_eq!("v1.2.3+10.gabcdef.modified", format!("{}", version));
            assert_eq!("v1.2.3+10.gabcdef.modified", format!("{:?}", version));
        }

        #[test]
        fn ontag_notmodified() {
            let version = GitInfo {
                tag_info: Some(TagInfo {
                    tag: "v1.2.3",
                    commits_since_tag: 0,
                }),
                commit_id: "abcdef",
                modified: false,
            };
            assert_eq!("v1.2.3+0.gabcdef", format!("{}", version));
            assert_eq!("v1.2.3+0.gabcdef", format!("{:?}", version));
        }

        #[test]
        fn ontag_modified() {
            let version = GitInfo {
                tag_info: Some(TagInfo {
                    tag: "v1.2.3",
                    commits_since_tag: 0,
                }),
                commit_id: "abcdef",
                modified: true,
            };
            assert_eq!("v1.2.3+0.gabcdef.modified", format!("{}", version));
            assert_eq!("v1.2.3+0.gabcdef.modified", format!("{:?}", version));
        }
    }
}