1#![cfg_attr(feature = "deny-warnings", deny(warnings))]
2
3/// This macro creates the version string during compilation from the
4/// current environment
5#[macro_export]
6macro_rules! get_version_info {
7 () => {{
8 let major = std::env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap();
9 let minor = std::env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap();
10 let patch = std::env!("CARGO_PKG_VERSION_PATCH").parse::<u16>().unwrap();
11 let crate_name = String::from(std::env!("CARGO_PKG_NAME"));
12
13 let host_compiler = std::option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string);
14 let commit_hash = std::option_env!("GIT_HASH").map(str::to_string);
15 let commit_date = std::option_env!("COMMIT_DATE").map(str::to_string);
16
17 $crate::VersionInfo {
18 major,
19 minor,
20 patch,
21 host_compiler,
22 commit_hash,
23 commit_date,
24 crate_name,
25 }
26 }};
27}
28
29/// This macro can be used in `build.rs` to automatically set the needed
30/// environment values, namely `GIT_HASH`, `COMMIT_DATE` and
31/// `RUSTC_RELEASE_CHANNEL`
32#[macro_export]
33macro_rules! setup_version_info {
34 () => {{
35 println!(
36 "cargo:rustc-env=GIT_HASH={}",
37 $crate::get_commit_hash().unwrap_or_default()
38 );
39 println!(
40 "cargo:rustc-env=COMMIT_DATE={}",
41 $crate::get_commit_date().unwrap_or_default()
42 );
43 println!("cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", $crate::get_channel());
44 }};
45}
46
47// some code taken and adapted from RLS and cargo
48pub struct VersionInfo {
49 pub major: u8,
50 pub minor: u8,
51 pub patch: u16,
52 pub host_compiler: Option<String>,
53 pub commit_hash: Option<String>,
54 pub commit_date: Option<String>,
55 pub crate_name: String,
56}
57
58impl std::fmt::Display for VersionInfo {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 let hash: String = self.commit_hash.clone().unwrap_or_default();
61 let hash_trimmed: &str = hash.trim();
62
63 let date: String = self.commit_date.clone().unwrap_or_default();
64 let date_trimmed: &str = date.trim();
65
66 if (hash_trimmed.len() + date_trimmed.len()) > 0 {
67 write!(
68 f,
69 "{} {}.{}.{} ({hash_trimmed} {date_trimmed})",
70 self.crate_name, self.major, self.minor, self.patch,
71 )?;
72 } else {
73 write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?;
74 }
75
76 Ok(())
77 }
78}
79
80impl std::fmt::Debug for VersionInfo {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 write!(
83 f,
84 "VersionInfo {{ crate_name: \"{}\", major: {}, minor: {}, patch: {}",
85 self.crate_name, self.major, self.minor, self.patch,
86 )?;
87 if self.commit_hash.is_some() {
88 write!(
89 f,
90 ", commit_hash: \"{}\", commit_date: \"{}\" }}",
91 self.commit_hash.clone().unwrap_or_default().trim(),
92 self.commit_date.clone().unwrap_or_default().trim()
93 )?;
94 } else {
95 write!(f, " }}")?;
96 }
97
98 Ok(())
99 }
100}
101
102#[must_use]
103pub fn get_commit_hash() -> Option<String> {
104 stdOption::process::Command::new(program:"git")
105 .args(["rev-parse", "--short", "HEAD"])
106 .output()
107 .ok()
108 .and_then(|r: Output| String::from_utf8(vec:r.stdout).ok())
109}
110
111#[must_use]
112pub fn get_commit_date() -> Option<String> {
113 stdOption::process::Command::new(program:"git")
114 .args(["log", "-1", "--date=short", "--pretty=format:%cd"])
115 .output()
116 .ok()
117 .and_then(|r: Output| String::from_utf8(vec:r.stdout).ok())
118}
119
120#[must_use]
121pub fn get_channel() -> String {
122 match std::env::var("CFG_RELEASE_CHANNEL") {
123 Ok(channel) => channel,
124 Err(_) => {
125 // if that failed, try to ask rustc -V, do some parsing and find out
126 match std::process::Command::new("rustc")
127 .arg("-V")
128 .output()
129 .ok()
130 .and_then(|r| String::from_utf8(r.stdout).ok())
131 {
132 Some(rustc_output) => {
133 if rustc_output.contains("beta") {
134 String::from("beta")
135 } else if rustc_output.contains("stable") {
136 String::from("stable")
137 } else {
138 // default to nightly if we fail to parse
139 String::from("nightly")
140 }
141 },
142 // default to nightly
143 None => String::from("nightly"),
144 }
145 },
146 }
147}
148
149#[cfg(test)]
150mod test {
151 use super::*;
152
153 #[test]
154 fn test_struct_local() {
155 let vi = get_version_info!();
156 assert_eq!(vi.major, 0);
157 assert_eq!(vi.minor, 2);
158 assert_eq!(vi.patch, 1);
159 assert_eq!(vi.crate_name, "rustc_tools_util");
160 // hard to make positive tests for these since they will always change
161 assert!(vi.commit_hash.is_none());
162 assert!(vi.commit_date.is_none());
163 }
164
165 #[test]
166 fn test_display_local() {
167 let vi = get_version_info!();
168 assert_eq!(vi.to_string(), "rustc_tools_util 0.3.0");
169 }
170
171 #[test]
172 fn test_debug_local() {
173 let vi = get_version_info!();
174 let s = format!("{vi:?}");
175 assert_eq!(
176 s,
177 "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 1 }"
178 );
179 }
180}
181