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

//! Command-line interface for the `git_tags_semver` library.
//!
//! # Features
//! The executable is only available with `executable` feature.

#![warn(missing_docs)]

use clap::Parser;
use std::path::Path;

#[derive(Parser, Debug)]
#[command(
    about = "Git Tags Semver\nTool for generating Version Numbers for different programming languages based on annotated git tags",
    arg_required_else_help = true,
    group(clap::ArgGroup::new("lang").required(true).multiple(false))
)]
struct Args {
    /// Generates the Version Number and just prints it on stdout.
    #[arg(long)]
    dry_run: bool,

    #[arg(short = 'o', long, value_name = "FILE")]
    out: String,

    #[arg(
        short = 'i',
        long,
        value_name = "PREFIX",
        help = "Path to header file target. Only required for C language"
    )]
    header_path: Option<String>,

    #[arg(
        short = 'I',
        long,
        value_name = "PREFIX",
        help = "Optional prefix that will be placed before <FILENAME>.h in the #include macro"
    )]
    include_prefix: Option<String>,

    #[arg(long, group = "lang")]
    rust: bool,
    #[arg(long, group = "lang")]
    golang: bool,
    #[arg(long, group = "lang")]
    text: bool,
    #[arg(long, group = "lang", requires = "header_path")]
    clang: bool,
}

fn build_include_string(args: &Args) -> String {
    let header_file = Path::new(args.header_path.as_ref().unwrap())
        .file_name()
        .unwrap_or_default()
        .to_string_lossy();

    match &args.include_prefix {
        Some(prefix) => format!("{}/{}", prefix.trim_end_matches('/'), header_file),
        None => header_file.into_owned(),
    }
}

fn main() {
    let args = Args::parse();

    let git_version =
        git_tags_semver::parse_git_describe().expect("Failed to parse git describe command");

    let lang: Box<dyn git_tags_semver::Lang> = if args.text {
        Box::new(git_tags_semver::PlainText)
    } else if args.rust {
        Box::new(git_tags_semver::Rust)
    } else if args.golang {
        Box::new(git_tags_semver::GoLang)
    } else if args.clang {
        Box::new(git_tags_semver::CLang {
            header_filename: build_include_string(&args),
        })
    } else {
        // This is unreachable because clap::ArgGroup guarantees that at least one
        // of the options inside the group is true
        unreachable!()
    };

    if args.dry_run {
        let version_string = git_tags_semver::create_full_lang_code(&git_version, &*lang);
        println!("{}", version_string);
        std::process::exit(0);
    }

    if let Some(header_file) = args.header_path {
        std::fs::write(header_file, lang.get_prelude()).expect("Failed to write header file");
        std::fs::write(args.out, lang.fmt_version_getter(&git_version))
            .expect("Failed to write source file");
    } else {
        std::fs::write(
            args.out,
            git_tags_semver::create_full_lang_code(&git_version, &*lang),
        )
        .expect("Failed to write to file");
    }
}