1use std::fmt;
4
5use serde::Serialize;
6
7#[derive(Serialize)]
9pub(crate) struct CommitInfo {
10 short_commit_hash: String,
11 commit_hash: String,
12 commit_date: String,
13 last_tag: Option<String>,
14 commits_since_last_tag: u32,
15}
16
17#[derive(Serialize)]
19pub struct SelfVersionInfo {
20 package_name: String,
22 version: String,
24 commit_info: Option<CommitInfo>,
28 target_triple: String,
30}
31
32impl SelfVersionInfo {
33 pub fn version(&self) -> &str {
35 &self.version
36 }
37}
38
39impl fmt::Display for SelfVersionInfo {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "{}", self.version)?;
46 if let Some(ci) = &self.commit_info {
47 if ci.commits_since_last_tag > 0 {
48 write!(f, "+{}", ci.commits_since_last_tag)?;
49 }
50 write!(
51 f,
52 " ({} {} {})",
53 ci.short_commit_hash, ci.commit_date, self.target_triple
54 )?;
55 } else {
56 write!(f, " ({})", self.target_triple)?;
57 }
58 Ok(())
59 }
60}
61
62impl fmt::Display for CommitInfo {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 if self.commits_since_last_tag > 0 {
65 write!(f, "+{}", self.commits_since_last_tag)?;
66 }
67 write!(f, " ({} {})", self.short_commit_hash, self.commit_date)?;
68 Ok(())
69 }
70}
71
72impl From<SelfVersionInfo> for clap::builder::Str {
73 fn from(val: SelfVersionInfo) -> Self {
74 val.to_string().into()
75 }
76}
77
78pub fn uv_self_version() -> SelfVersionInfo {
80 macro_rules! option_env_str {
82 ($name:expr) => {
83 option_env!($name).map(|s| s.to_string())
84 };
85 }
86
87 let version = uv_version::version().to_string();
89
90 let commit_info = option_env_str!("UV_COMMIT_HASH").map(|commit_hash| CommitInfo {
92 short_commit_hash: option_env_str!("UV_COMMIT_SHORT_HASH").unwrap(),
93 commit_hash,
94 commit_date: option_env_str!("UV_COMMIT_DATE").unwrap(),
95 last_tag: option_env_str!("UV_LAST_TAG"),
96 commits_since_last_tag: option_env_str!("UV_LAST_TAG_DISTANCE")
97 .as_deref()
98 .map_or(0, |value| value.parse::<u32>().unwrap_or(0)),
99 });
100
101 let target_triple = env!("RUST_HOST_TARGET").to_string();
103
104 SelfVersionInfo {
105 package_name: "uv".to_owned(),
106 version,
107 commit_info,
108 target_triple,
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use insta::{assert_json_snapshot, assert_snapshot};
115
116 use super::{CommitInfo, SelfVersionInfo};
117
118 #[test]
119 fn version_formatting() {
120 let version = SelfVersionInfo {
121 package_name: "uv".to_string(),
122 version: "0.0.0".to_string(),
123 commit_info: None,
124 target_triple: "x86_64-unknown-linux-gnu".to_string(),
125 };
126 assert_snapshot!(version, @"0.0.0 (x86_64-unknown-linux-gnu)");
127 }
128
129 #[test]
130 fn version_formatting_with_commit_info() {
131 let version = SelfVersionInfo {
132 package_name: "uv".to_string(),
133 version: "0.0.0".to_string(),
134 commit_info: Some(CommitInfo {
135 short_commit_hash: "53b0f5d92".to_string(),
136 commit_hash: "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7".to_string(),
137 last_tag: Some("v0.0.1".to_string()),
138 commit_date: "2023-10-19".to_string(),
139 commits_since_last_tag: 0,
140 }),
141 target_triple: "x86_64-unknown-linux-gnu".to_string(),
142 };
143 assert_snapshot!(version, @"0.0.0 (53b0f5d92 2023-10-19 x86_64-unknown-linux-gnu)");
144 }
145
146 #[test]
147 fn version_formatting_with_commits_since_last_tag() {
148 let version = SelfVersionInfo {
149 package_name: "uv".to_string(),
150 version: "0.0.0".to_string(),
151 commit_info: Some(CommitInfo {
152 short_commit_hash: "53b0f5d92".to_string(),
153 commit_hash: "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7".to_string(),
154 last_tag: Some("v0.0.1".to_string()),
155 commit_date: "2023-10-19".to_string(),
156 commits_since_last_tag: 24,
157 }),
158 target_triple: "x86_64-unknown-linux-gnu".to_string(),
159 };
160 assert_snapshot!(version, @"0.0.0+24 (53b0f5d92 2023-10-19 x86_64-unknown-linux-gnu)");
161 }
162
163 #[test]
164 fn version_serializable() {
165 let version = SelfVersionInfo {
166 package_name: "uv".to_string(),
167 version: "0.0.0".to_string(),
168 commit_info: Some(CommitInfo {
169 short_commit_hash: "53b0f5d92".to_string(),
170 commit_hash: "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7".to_string(),
171 last_tag: Some("v0.0.1".to_string()),
172 commit_date: "2023-10-19".to_string(),
173 commits_since_last_tag: 0,
174 }),
175 target_triple: "x86_64-unknown-linux-gnu".to_string(),
176 };
177 assert_json_snapshot!(version, @r#"
178 {
179 "package_name": "uv",
180 "version": "0.0.0",
181 "commit_info": {
182 "short_commit_hash": "53b0f5d92",
183 "commit_hash": "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7",
184 "commit_date": "2023-10-19",
185 "last_tag": "v0.0.1",
186 "commits_since_last_tag": 0
187 },
188 "target_triple": "x86_64-unknown-linux-gnu"
189 }
190 "#);
191 }
192}