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