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}