use crate::prelude::*;
pub const APP_NAME: &str = env!("CARGO_PKG_NAME");
const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const APP_HOMEPAGE: &str = env!("CARGO_PKG_HOMEPAGE");
const GIT_DESCRIBE: Option<&str> = option_env!("CAESURA_GIT_DESCRIBE");
const DEV_VERSION: &str = "0.0.0";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BuildStatus {
Release,
Unmodified,
Modified,
Unknown,
}
impl Display for BuildStatus {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str(self.as_str())
}
}
impl BuildStatus {
fn as_str(self) -> &'static str {
match self {
Self::Release => "release",
Self::Unmodified => "unmodified",
Self::Modified => "modified",
Self::Unknown => "unknown",
}
}
pub fn get() -> Self {
Self::from_version_and_describe(APP_VERSION, GIT_DESCRIBE)
}
fn from_version_and_describe(version: &str, describe: Option<&str>) -> Self {
if version != DEV_VERSION {
return Self::Release;
}
let Some(desc) = describe else {
return Self::Unknown;
};
if desc.contains("-dirty") {
return Self::Modified;
}
if desc.contains("-g") {
return Self::Modified;
}
if desc.starts_with('v') {
return Self::Unmodified;
}
Self::Unknown
}
}
pub fn app_version_or_describe() -> String {
if APP_VERSION != DEV_VERSION {
return format!("v{APP_VERSION}");
}
GIT_DESCRIBE.unwrap_or(DEV_VERSION).to_owned()
}
pub fn app_user_agent(include_url: bool) -> String {
let status = BuildStatus::get();
let mut parts: Vec<&str> = vec![status.as_str()];
if status != BuildStatus::Release {
parts.push(GIT_DESCRIBE.unwrap_or("unknown"));
}
if include_url {
parts.push(APP_HOMEPAGE);
}
let comment = parts.join("; ");
format!("{APP_NAME}/{APP_VERSION} ({comment})")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_release() {
assert_eq!(BuildStatus::Release.to_string(), "release");
}
#[test]
fn display_unmodified() {
assert_eq!(BuildStatus::Unmodified.to_string(), "unmodified");
}
#[test]
fn display_modified() {
assert_eq!(BuildStatus::Modified.to_string(), "modified");
}
#[test]
fn display_unknown() {
assert_eq!(BuildStatus::Unknown.to_string(), "unknown");
}
#[test]
fn build_status_release_version() {
assert_eq!(
BuildStatus::from_version_and_describe("1.2.3", None),
BuildStatus::Release
);
}
#[test]
fn build_status_release_ignores_describe() {
assert_eq!(
BuildStatus::from_version_and_describe("1.2.3", Some("v1.2.3-dirty")),
BuildStatus::Release,
);
}
#[test]
fn build_status_dev_no_git() {
assert_eq!(
BuildStatus::from_version_and_describe("0.0.0", None),
BuildStatus::Unknown
);
}
#[test]
fn build_status_dev_clean_tag() {
assert_eq!(
BuildStatus::from_version_and_describe("0.0.0", Some("v0.26.0")),
BuildStatus::Unmodified,
);
}
#[test]
fn build_status_dev_dirty_tag() {
assert_eq!(
BuildStatus::from_version_and_describe("0.0.0", Some("v0.26.0-dirty")),
BuildStatus::Modified,
);
}
#[test]
fn build_status_dev_commits_ahead() {
assert_eq!(
BuildStatus::from_version_and_describe("0.0.0", Some("v0.26.0-3-gabcdef")),
BuildStatus::Modified,
);
}
#[test]
fn build_status_dev_commits_ahead_dirty() {
assert_eq!(
BuildStatus::from_version_and_describe("0.0.0", Some("v0.26.0-3-gabcdef-dirty")),
BuildStatus::Modified,
);
}
#[test]
fn build_status_dev_hash_only() {
assert_eq!(
BuildStatus::from_version_and_describe("0.0.0", Some("abcdef")),
BuildStatus::Unknown,
);
}
#[test]
fn app_user_agent_contains_status() {
let ua = app_user_agent(true);
let status = BuildStatus::get();
assert!(ua.contains(&status.to_string()));
}
#[test]
fn app_user_agent_contains_name_and_version() {
let ua = app_user_agent(true);
assert!(ua.contains(APP_NAME));
assert!(ua.contains(APP_VERSION));
}
#[test]
fn app_name_is_populated() {
assert!(!APP_NAME.is_empty());
}
#[test]
fn app_version_is_populated() {
assert!(!APP_VERSION.is_empty());
}
#[test]
fn app_homepage_is_populated() {
assert!(!APP_HOMEPAGE.is_empty());
}
}