git-tags-semver 0.6.2

Tool to extract SemVer Version Information from annotated git tags
Documentation
// Copyright Open Logistics Foundation
//
// Licensed under the Open Logistics Foundation License 1.3.
// For details on the licensing terms, see the LICENSE file.
// SPDX-License-Identifier: OLFL-1.3

// When used in a build.rs, we use std features for convenience. When used in a (possibly no_std)
// firmware or application, this is a no_std lib which only defines the SemanticVersion and
// GitVersion types.
#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]

//! Tool to extract SemVer Version Information from annotated git tags
//! This package allows to extract SemVer version information from annotated git tags in the format
//! "vX.Y.Z". It is used from build.rs build scripts. For usage details, see the API documentation.
//!
//! This crate is made to call and parse the git describe command. It is assumed that the repo uses
//! _annotated_ git tags in the format "vX.Y.Z" or "vX.Y.Z-rc". So for example, the first commit of
//! the release candidate branch for 1.0.0 should be tagged with "v1.0.0-rc" and the actual 1.0.0
//! release should be tagged with "v1.0.0". This information has to be extracted at compile time so
//! it needs to be done in a `build.rs`. Since the `build.rs` is run in the directory of the crate
//! it belongs to, your project itself should contain that `build.rs`, i.e. it should not be done
//! in a git submodule or a dependency of your project. The following `build.rs` will write the
//! version information to `version.rs` in the current `OUT_DIR` directory which is
//! ["the folder in which all output and intermediate artifacts should be placed"](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts)
//! by build scripts:
//!
//! ```
//! use std::env;
//! use std::fs;
//! use std::path::Path;
//! use std::process::Command;
//! use git_tags_semver::{Rust, Lang};
//!
//! # // The test only works if the "build" feature is activated. To prevent the test from failing,
//! # // we add an empty main function below if it is not activated.
//! fn main() {
//!     // Tell Cargo that if the given file changes, to rerun this build script (adjust the path
//!     // to the .git directory accordingly if required, e.g. when the crate is in a workspace)
//!     println!("cargo:rerun-if-changed=.git/index");
//!     println!("cargo:rerun-if-changed=.git/HEAD");
//!     println!("cargo:rerun-if-changed=.git/logs/HEAD");
//!     println!("cargo:rerun-if-changed=build.rs");
//!
//!     // Extract the version information and generate the corresponding code
//!     let out = Rust.fmt_version_getter(&git_tags_semver::parse_git_describe().unwrap());
//! #    // We are not in a true build.rs here, so set the required environment variables manually
//! #    unsafe { env::set_var("OUT_DIR", env::temp_dir()); };
//!     
//!     // Write the generated version information code to version.rs in the OUT_DIR directory
//!     let out_dir = env::var_os("OUT_DIR").unwrap();
//!     let dest_path = Path::new(&out_dir).join("version.rs");
//!     fs::write(&dest_path, &out).unwrap();
//!
//!     // Touch build.rs to force recompilation on every workspace build. This may be desirable
//!     // because it ensures that the information is guaranteed to be up-to-date. On the other
//!     // hand, it may slow down your development process a tiny little bit because it enforces
//!     // a rebuild all the time. For example, your crate will be rebuilt before each `cargo run`
//!     // which does not happen normally (if the crate was built before, it can normally be run
//!     // immediately).
//!     Command::new("touch")
//!         .args(&["build.rs"])
//!         .output()
//!         .expect("Could not touch build.rs!");
//! #    // Currently, this crate does not have a build.rs so we have to clean up after ourselves
//! #    Command::new("rm").args(&["build.rs"]).output()
//! #    .expect("Could not clean up, i.e. remove build.rs");
//! }
//! ```
//!
//! Afterwards, `<OUT_DIR>/version.rs` contains a `pub fn get_version() ->` [`GitVersion`](GitVersion).
//! Specifically, that file looks like:
//!
//! ```ignore
//! pub fn get_version() -> git_tags_semver::GitVersion {
//!     git_tags_semver::GitVersion {
//!         semver: Some(git_tags_semver::SemanticVersion {
//!             major: 1,
//!             minor: 0,
//!             patch: 0,
//!             rc: false,
//!             commits: 0
//!         }),
//!         hash: [
//!             0x12,
//!             0x34,
//!             0xab,
//!             0xcd,
//!         ],
//!         dirty: false,
//!         git_string: "v1.0.0-00-g1234abcd"
//!     }
//! }
//! ```
//!
//! To use it in your application, you can include the generated code like that:
//!
//! ```ignore
//! include!(concat!(env!("OUT_DIR"), "/version.rs"));
//!
//! fn main() {
//!     println!("{:?}", get_version());
//! }
//! ```
//!
//! # Cargo.toml / Feature-Gates
//!
//! This tool is especially designed to be used in `no_std` contexts, e.g. bare-metal firmwares.
//! Therefore, it is generally `no_std` (the type definitions) but for extracting and writing the
//! version information to an intermediate file, it needs the standard library (and `build.rs` is
//! run with `std`, even for `no_std` packages). Thus, the standard library usage which enables the
//! part required in the `build.rs` has to be enabled with the "build" feature. So, for a `no_std`
//! package, you need to add it as a dependency twice, the corresponding part in your `Cargo.toml`
//! could look like this:
//!
//! ```toml
//! [dependencies]
//! git-tags-semver = { version = "1.0.0", default-features = false }
//!
//! [build-dependencies]
//! git-tags-semver = { version = "1.0.0" }
//! ```
//!
//! Note that this additionally requires to use
//! [feature resolver version 2](https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2)
//! for your project which is
//! [the default since Rust's 2021 edition](https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions)
//! or can be specified with `resolver = "2"` in your `Cargo.toml` (if using a workspace, this must
//! be done in the top-level/workspace `Cargo.toml`).
//!
//! # License
//!
//! Open Logistics Foundation License
//! Version 1.3, January 2023\
//! See the LICENSE file in the top-level directory.
//!
//! # Contact
//!
//! Fraunhofer IML Embedded Rust Group - <embedded-rust@iml.fraunhofer.de>

#[cfg(feature = "std")]
mod generator;
#[cfg(feature = "std")]
mod parser;
mod types;

pub use heapless;

#[cfg(feature = "std")]
pub use crate::generator::{CLang, GoLang, Lang, PlainText, Rust};
pub use crate::types::{GitVersion, SemanticVersion, VersionError, VersionResult, VersionString};

#[cfg(all(feature = "std", not(feature = "executable")))]
#[deprecated]
/// Create the Rust getter function of the `GitVersion` struct
///
/// This function is deprecated. Instead you should use the following (this is exactly what is done
/// in this function):
/// ```rust
/// use git_tags_semver::{Rust, Lang};
/// let out = Rust.fmt_version_getter(&git_tags_semver::parse_git_describe().unwrap());
/// ```
pub fn get_version_file_content() -> VersionResult<String> {
    Ok(Rust.fmt_version_getter(&parse_git_describe().unwrap()))
}

#[cfg(feature = "std")]
/// Run and parse the `git describe` command to build a `GitVersion` struct
pub fn parse_git_describe() -> VersionResult<GitVersion> {
    crate::parser::parse_git_describe(crate::parser::run_git_describe()?.as_str())
}

#[cfg(feature = "std")]
/// Create the full language code, including the prelude
pub fn create_full_lang_code(git_version: &GitVersion, lang: &dyn Lang) -> String {
    format!(
        "{}\n\n{}",
        lang.get_prelude(),
        lang.fmt_version_getter(git_version),
    )
}