sequencer_utils/
build_info.rs

1use std::fmt;
2
3pub const GIT_DESCRIBE: &str = env!("VERGEN_GIT_DESCRIBE");
4pub const GIT_SHA: &str = env!("VERGEN_GIT_SHA");
5pub const GIT_DIRTY: &str = env!("VERGEN_GIT_DIRTY");
6pub const GIT_BRANCH: &str = env!("VERGEN_GIT_BRANCH");
7pub const GIT_COMMIT_TIMESTAMP: &str = env!("VERGEN_GIT_COMMIT_TIMESTAMP");
8
9/// Build and git metadata for version output.
10///
11/// `Display` prints only build details (git, os, arch) without `pkg_name` or
12/// `pkg_version`. Git tags are used for binary versioning, not Cargo package
13/// versions, so we omit them to avoid confusion (e.g., showing "0.1.0" when the
14/// actual release tag is "20260223").
15///
16/// Use [`BuildInfo::with_header`] to include `pkg_name` as a header line:
17///
18/// ```ignore
19/// print!("{}", sequencer_utils::build_info!().with_header());
20/// ```
21///
22/// For clap's `long_version`, use [`BuildInfo::clap_version`] which prepends a
23/// newline (clap already prints the binary name):
24///
25/// ```ignore
26/// #[command(long_version = sequencer_utils::build_info!().clap_version())]
27/// ```
28pub struct BuildInfo {
29    pub pkg_name: &'static str,
30    pub pkg_version: &'static str,
31    pub git_describe: &'static str,
32    pub git_sha: &'static str,
33    pub git_dirty: &'static str,
34    pub git_branch: &'static str,
35    pub git_commit_timestamp: &'static str,
36    pub is_debug: bool,
37    pub os: &'static str,
38    pub arch: &'static str,
39}
40
41impl BuildInfo {
42    pub fn with_header(&self) -> String {
43        format!("{}\n{self}", self.pkg_name)
44    }
45
46    pub fn clap_version(&self) -> String {
47        format!("\n{self}")
48    }
49}
50
51impl fmt::Display for BuildInfo {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        writeln!(f, "describe: {}", self.git_describe)?;
54        writeln!(f, "rev: {}", self.git_sha)?;
55        writeln!(f, "modified: {}", self.git_dirty)?;
56        writeln!(f, "branch: {}", self.git_branch)?;
57        writeln!(f, "commit-timestamp: {}", self.git_commit_timestamp)?;
58        writeln!(f, "debug: {}", self.is_debug)?;
59        writeln!(f, "os: {}", self.os)?;
60        write!(f, "arch: {}", self.arch)
61    }
62}
63
64#[macro_export]
65macro_rules! build_info {
66    () => {
67        $crate::build_info::BuildInfo {
68            pkg_name: env!("CARGO_PKG_NAME"),
69            pkg_version: env!("CARGO_PKG_VERSION"),
70            git_describe: $crate::build_info::GIT_DESCRIBE,
71            git_sha: $crate::build_info::GIT_SHA,
72            git_dirty: $crate::build_info::GIT_DIRTY,
73            git_branch: $crate::build_info::GIT_BRANCH,
74            git_commit_timestamp: $crate::build_info::GIT_COMMIT_TIMESTAMP,
75            is_debug: cfg!(debug_assertions),
76            os: std::env::consts::OS,
77            arch: std::env::consts::ARCH,
78        }
79    };
80}
81
82#[cfg(test)]
83mod test {
84    #[test]
85    fn test_build_info_display() {
86        let info = crate::build_info!();
87        let output = info.to_string();
88        for field in [
89            "describe:",
90            "rev:",
91            "modified:",
92            "branch:",
93            "commit-timestamp:",
94            "debug:",
95            "os:",
96            "arch:",
97        ] {
98            assert!(output.contains(field), "missing {field}: {output}");
99        }
100        assert!(output.starts_with("describe:"));
101        assert!(!output.contains("sequencer-utils"));
102        assert_eq!(info.pkg_name, "sequencer-utils");
103        assert!(!info.git_sha.is_empty());
104        assert!(!info.os.is_empty());
105        assert!(!info.arch.is_empty());
106    }
107
108    #[test]
109    fn test_clap_version() {
110        let info = crate::build_info!();
111        let clap_ver = info.clap_version();
112        assert!(clap_ver.starts_with('\n'),);
113        assert!(!clap_ver.contains("sequencer-utils"),);
114        assert!(!clap_ver.contains("0.1.0"),);
115        assert!(clap_ver.contains("describe:"));
116    }
117}