git_sha1/lib.rs
1#![allow(clippy::needless_doctest_main)]
2//! Provide the current Git commit SHA1 during build.
3//!
4//! When building crates from a Git repository it is often desirable to extract the
5//! current version number in form of the Git SHA1 to be displayed. This crate extracts
6//! the current Git SHA1 during build time and makes it accessible as an environment
7//! variable.
8//!
9//! If the crate is currently built without access to the Git SHA1 (i. e. it was extracted
10//! from a tar-archive, or Git is not installed), instead of failing, it falls back on a
11//! default value. This value defaults to "", but can be changed with the
12//! [`use_default()`](struct.GitSHA1.html#method.use_default) method.
13//!
14//!
15//! # Example
16//!
17//! In `build.rs`:
18//! ```
19//! use git_sha1::GitSHA1;
20//!
21//! fn main() {
22//! GitSHA1::read().set("GIT_SHA1");
23//! }
24//! ```
25//!
26//! In `main.rs`:
27//! ```
28//! use git_sha1::GitSHA1;
29//!
30//! // either as static &str:
31//! static SHA1: &str = env!("GIT_SHA1");
32//!
33//! // or during runtime:
34//! fn main() {
35//! let sha1 = GitSHA1::from_env("GIT_SHA1");
36//!
37//! let long = sha1.long();
38//! assert_eq!(SHA1, long);
39//!
40//! let short = sha1.short(10);
41//! // `short` may be shorter if SHA1 does not exist
42//! assert_eq!(short.len(), usize::min(10, short.len()));
43//! }
44//! ```
45//!
46
47use std::process::Command;
48
49/// *deprecated*
50///
51/// Number of hex-characters in the Git SHA1.
52///
53/// There are half as many __bytes__ in a SHA1.
54///
55/// This value is true for any Git SHA1, but must not necessarily be the number of digits
56/// available in the extracted value. Either because Git is not even installed, or because
57/// the crate is not compiled from a Git repository and the implementation falls back on a
58/// default string to return instead.
59///
60#[allow(dead_code)]
61pub const GIT_SHA1_LENGTH: usize = 40;
62
63pub struct GitSHA1 {
64 /// default value that can be used instead of the real Git SHA1, if the crate is not
65 /// in a Git repository (i. e. when building from a .tar-archive)
66 default: String,
67 /// Contains `None` if the current crate does not contain a Git SHA1.
68 sha1: Option<String>,
69}
70
71impl GitSHA1 {
72 /// Although designed with Git in mind, your crate may be compiled from source without
73 /// being in a Git repository. This lets you define the value which is used instead of
74 /// the real Git SHA1 as a drop-in replacement. The default value is "" (an empty
75 /// string).
76 ///
77 /// # Example
78 ///
79 /// ```
80 /// # use git_sha1::GitSHA1;
81 /// GitSHA1::read().use_default("<empty>").set("GIT_SHA1");
82 /// // will use: GIT_SHA1="<empty>"
83 /// ```
84 ///
85 #[allow(dead_code)] // not used here
86 pub fn use_default(mut self, default: &str) -> Self {
87 self.default = default.into();
88 self
89 }
90
91 /// Read current Git SHA1 by running `git rev-parse HEAD`.
92 ///
93 /// This only retrieves the SHA1 from git. Use
94 /// [`set(name)`](struct.GitSHA1.html#method.set) to pass compile-time environment
95 /// variable to cargo-build. Afterwards it can be read from your source files.
96 ///
97 /// If the current crate is not a git repository, the read will silently fail and the
98 /// returned string in the end will be substituded by a default value.
99 ///
100 pub fn read() -> Self {
101 let sha1: Option<String> = Command::new("git")
102 .args(&["rev-parse", "HEAD"])
103 .output()
104 .map_or(None, |out| {
105 Some(String::from_utf8(out.stdout).unwrap().trim().into())
106 });
107
108 Self {
109 default: String::new(),
110 sha1,
111 }
112 }
113
114 /// Read the comiple-time environment variable from carto-build. This can be used
115 /// during runtime, i.e. in `fn main() {}`.
116 ///
117 /// # Note:
118 ///
119 /// You can read the Git SHA1 statically using `env!`:
120 ///
121 /// ```
122 /// // "GIT_SHA1" set in `build.rs`
123 /// const GIT_SHA1: &'static str = env!("GIT_SHA1");
124 /// ```
125 #[allow(dead_code)] // because we never use it in `build.rs`
126 pub fn from_env(name: &str) -> Self {
127 Self {
128 default: String::new(),
129 sha1: std::env::var(name).ok(),
130 }
131 }
132
133 /// Generate cargo environment variable for long Git SHA1.
134 ///
135 /// # Example
136 ///
137 /// ```
138 /// # use git_sha1::GitSHA1;
139 /// GitSHA1::read().set("GIT_SHA1");
140 /// ```
141 pub fn set(&self, name: &str) {
142 println!(
143 "cargo:rustc-env={}={}",
144 name,
145 self.sha1.as_ref().unwrap_or(&self.default)
146 );
147 }
148
149 /// Return the complete SHA1 as a String.
150 #[allow(dead_code)] // because we never use it in `build.rs`
151 pub fn long(&self) -> String {
152 self.sha1.as_ref().unwrap_or(&self.default).clone()
153 }
154
155 /// Return a subset of digits (short sha) as a String.
156 ///
157 /// # Note
158 ///
159 /// The number of digits extracted is silently truncated to the maximum number of
160 /// digits available in the Git SHA1.
161 ///
162 #[allow(dead_code)] // because we never use it in `build.rs`
163 pub fn short(&self, count: usize) -> String {
164 match &self.sha1 {
165 None => self.default.clone(),
166 Some(s) => {
167 let count = usize::min(s.len(), count);
168 s.as_str()[..count].into()
169 }
170 }
171 }
172}