gitbox 2.1.3

Git toolbox to simplify adoption of conventional commits and semantic version, among other things.
Documentation
use clap::{builder::PossibleValue, Args, ValueEnum};

use crate::{
    application::{
        controller::{describe::DescribeController, exit_code::ControllerExitCode},
        manager::{
            gitinfo_ingress_manager::GitInfoIngressManager,
            message_egress_manager::MessageEgressManager,
        },
        options::describe::{
            DescribeMetadataOptions, DescribeOptions, DescribePrereleaseOptions,
            DescribeTagOptions, DescribeTriggerOptions,
        },
    },
    infrastructure::{
        interface::{git_cli::GitCli, message_egress_manager_impl::MessageEgressManagerImpl},
        subcommand::Subcommand,
    },
    usecase::metadata_spec::MetadataSpec,
};

#[derive(Args, Debug)]
#[command(about = "Calculate the next version")]
pub struct DescribeSubCommand {
    #[arg(long, help = "Describe a prerelease")]
    prerelease: bool,

    #[arg(
        long,
        help = "Set the pattern for the new prerelease. A number can be used with the placeholder '%d'",
        requires("prerelease"),
        default_value = "%d"
    )]
    prerelease_pattern: String,
    #[arg(
        long,
        help = "Set the pattern of the old prerelease. Uses the same placeholder as '--prerelease-pattern'. Use this option when changing prerelease pattern. Defaults to the prerelease pattern",
        requires("prerelease")
    )]
    old_prerelease_pattern: Option<String>,

    #[arg(
        short,
        long,
        help = "Print the last version (if possible) in addition to the new version"
    )]
    diff: bool,

    #[arg(short, long, help = "Add a metadata to include in the new version (can be used multiple times)", value_parser = clap::builder::EnumValueParser::<MetadataSpec>::new())]
    metadata: Vec<MetadataSpec>,

    #[arg(
        long,
        help = "Set the expression which triggers a major change (Default behaviour is equivalent to 'breaking'). For more informations about the grammar, run 'help grammar'"
    )]
    major_trigger: Option<String>,
    #[arg(
        long,
        help = "Set the expression which triggers a minor change (Default behaviour is equivalent to 'type IN [ feat ]'). For more informations about the grammar, run 'help grammar'"
    )]
    minor_trigger: Option<String>,
    #[arg(
        long,
        help = "Set the expression which triggers a patch change (Default behaviou is equivalent to 'type IN [ fix ]'). For more informations about the grammar, run 'help grammar'"
    )]
    patch_trigger: Option<String>,

    #[arg(short = 't', long, help = "Create a new tag with the computed version")]
    create_tag: bool,
    #[arg(
        short = 'M',
        long,
        help = "Set the additional message for the created tag",
        requires("create_tag"),
        value_parser = clap::builder::NonEmptyStringValueParser::new()
    )]
    tag_message: Option<String>,
    #[arg(
        short = 's',
        long,
        help = "If set, the created tag is signed",
        requires("create_tag")
    )]
    sign_tag: bool,
}

impl Subcommand for DescribeSubCommand {
    fn execute(&self) -> i32 {
        let git_cli = GitCli::new();
        let output_manager = MessageEgressManagerImpl::new();
        if let Err(e) = git_cli.git_dir() {
            output_manager.error(&format!("Failed to retrieve git dir: {}", e));
            output_manager.error("describe subcommand can only be run inside a git project");
            return 1;
        }
        match DescribePrereleaseOptions::new(
            self.prerelease,
            self.prerelease_pattern.clone(),
            self.old_prerelease_pattern
                .clone()
                .unwrap_or(self.prerelease_pattern.clone()),
        ) {
            Ok(prerelease_options) => {
                let options = DescribeOptions::new(
                    prerelease_options,
                    self.diff,
                    DescribeMetadataOptions::new(self.metadata.clone()),
                    DescribeTriggerOptions::new(
                        self.major_trigger.clone(),
                        self.minor_trigger.clone(),
                        self.patch_trigger.clone(),
                    ),
                    DescribeTagOptions::new(
                        self.create_tag,
                        self.tag_message.clone(),
                        self.sign_tag,
                    ),
                );
                let controller = DescribeController::new(
                    options,
                    &git_cli,
                    &git_cli,
                    &git_cli,
                    &git_cli,
                    &output_manager,
                );
                match controller.describe() {
                    ControllerExitCode::Ok => 0,
                    ControllerExitCode::Error(i) => i,
                }
            }
            Err(e) => {
                output_manager.error(&e.to_string());
                1
            }
        }
    }
}

impl ValueEnum for MetadataSpec {
    fn value_variants<'a>() -> &'a [Self] {
        &[Self::Sha, Self::Date]
    }

    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
        match self {
            Self::Sha => Some(PossibleValue::new("sha")),
            Self::Date => Some(PossibleValue::new("date")),
        }
    }
}