git-sha1 1.1.0

Expose the Git-SHA1 to the crate during build-time.
Documentation
#![allow(clippy::needless_doctest_main)]
//! Provide the current Git commit SHA1 during build.
//!
//! When building crates from a Git repository it is often desirable to extract the
//! current version number in form of the Git SHA1 to be displayed. This crate extracts
//! the current Git SHA1 during build time and makes it accessible as an environment
//! variable.
//!
//! If the crate is currently built without access to the Git SHA1 (i. e. it was extracted
//! from a tar-archive, or Git is not installed), instead of failing, it falls back on a
//! default value. This value defaults to "", but can be changed with the
//! [`use_default()`](struct.GitSHA1.html#method.use_default) method.
//!
//!
//! # Example
//!
//! In `build.rs`:
//! ```
//! use git_sha1::GitSHA1;
//!
//! fn main() {
//!     GitSHA1::read().set("GIT_SHA1");
//! }
//! ```
//!
//! In `main.rs`:
//! ```
//! use git_sha1::GitSHA1;
//!
//! // either as static &str:
//! static SHA1: &str = env!("GIT_SHA1");
//!
//! // or during runtime:
//! fn main() {
//!     let sha1 = GitSHA1::from_env("GIT_SHA1");
//!
//!     let long = sha1.long();
//!     assert_eq!(SHA1, long);
//!
//!     let short = sha1.short(10);
//!     // `short` may be shorter if SHA1 does not exist
//!     assert_eq!(short.len(), usize::min(10, short.len()));
//! }
//! ```
//!

use std::process::Command;

/// *deprecated*
///
/// Number of hex-characters in the Git SHA1.
///
/// There are half as many __bytes__ in a SHA1.
///
/// This value is true for any Git SHA1, but must not necessarily be the number of digits
/// available in the extracted value. Either because Git is not even installed, or because
/// the crate is not compiled from a Git repository and the implementation falls back on a
/// default string to return instead.
///
#[allow(dead_code)]
pub const GIT_SHA1_LENGTH: usize = 40;

pub struct GitSHA1 {
    /// default value that can be used instead of the real Git SHA1, if the crate is not
    /// in a Git repository (i. e. when building from a .tar-archive)
    default: String,
    /// Contains `None` if the current crate does not contain a Git SHA1.
    sha1: Option<String>,
}

impl GitSHA1 {
    /// Although designed with Git in mind, your crate may be compiled from source without
    /// being in a Git repository. This lets you define the value which is used instead of
    /// the real Git SHA1 as a drop-in replacement. The default value is "" (an empty
    /// string).
    ///
    /// # Example
    ///
    /// ```
    /// # use git_sha1::GitSHA1;
    /// GitSHA1::read().use_default("<empty>").set("GIT_SHA1");
    /// // will use: GIT_SHA1="<empty>"
    /// ```
    ///
    #[allow(dead_code)] // not used here
    pub fn use_default(mut self, default: &str) -> Self {
        self.default = default.into();
        self
    }

    /// Read current Git SHA1 by running `git rev-parse HEAD`.
    ///
    /// This only retrieves the SHA1 from git. Use
    /// [`set(name)`](struct.GitSHA1.html#method.set) to pass compile-time environment
    /// variable to cargo-build. Afterwards it can be read from your source files.
    ///
    /// If the current crate is not a git repository, the read will silently fail and the
    /// returned string in the end will be substituded by a default value.
    ///
    pub fn read() -> Self {
        let sha1: Option<String> = Command::new("git")
            .args(&["rev-parse", "HEAD"])
            .output()
            .map_or(None, |out| {
                Some(String::from_utf8(out.stdout).unwrap().trim().into())
            });

        Self {
            default: String::new(),
            sha1,
        }
    }

    /// Read the comiple-time environment variable from carto-build. This can be used
    /// during runtime, i.e. in `fn main() {}`.
    ///
    /// # Note:
    ///
    /// You can read the Git SHA1 statically using `env!`:
    ///
    /// ```
    /// // "GIT_SHA1" set in `build.rs`
    /// const GIT_SHA1: &'static str = env!("GIT_SHA1");
    /// ```
    #[allow(dead_code)] // because we never use it in `build.rs`
    pub fn from_env(name: &str) -> Self {
        Self {
            default: String::new(),
            sha1: std::env::var(name).ok(),
        }
    }

    /// Generate cargo environment variable for long Git SHA1.
    ///
    /// # Example
    ///
    /// ```
    /// # use git_sha1::GitSHA1;
    /// GitSHA1::read().set("GIT_SHA1");
    /// ```
    pub fn set(&self, name: &str) {
        println!(
            "cargo:rustc-env={}={}",
            name,
            self.sha1.as_ref().unwrap_or(&self.default)
        );
    }

    /// Return the complete SHA1 as a String.
    #[allow(dead_code)] // because we never use it in `build.rs`
    pub fn long(&self) -> String {
        self.sha1.as_ref().unwrap_or(&self.default).clone()
    }

    /// Return a subset of digits (short sha) as a String.
    ///
    /// # Note
    ///
    /// The number of digits extracted is silently truncated to the maximum number of
    /// digits available in the Git SHA1.
    ///
    #[allow(dead_code)] // because we never use it in `build.rs`
    pub fn short(&self, count: usize) -> String {
        match &self.sha1 {
            None => self.default.clone(),
            Some(s) => {
                let count = usize::min(s.len(), count);
                s.as_str()[..count].into()
            }
        }
    }
}