ck3-regions 0.1.2

Generates title-based region textures for use with the custom dynamic terrain shader system implemented in some CK3 mods.
Documentation
use std::path::PathBuf;

use clap::{Args, ArgAction, Parser};

use crate::{
    model::game::TitleTier,
    config,
};

//
// pub struct Cli
//

#[derive(Debug, Parser)]
#[command(author, version, about = "Region map textures generator for Crusader Kings 3 mods", long_about = None)]
pub struct Cli {
    #[command(flatten)]
    pub mode_arg: ModeArg,

    #[arg(short = 'p', long, value_name = "MOD_PREFIX", value_parser = validate_mod_prefix, default_value = "MOD")]
    pub mod_prefix: String,

    #[arg(value_name = "MOD_ROOT_PATH")]
    pub mod_root_path: PathBuf,

    #[arg(long, value_name = "PROXIMITY_LIMIT", default_value = "5.0")]
    pub proximity_limit: f32,

    #[arg(short, long = "output-metadata", value_name = "METADATA_YAML_FILE")]
    pub output_metadata_yaml_path: Option<PathBuf>,

    #[arg(short = 'E', long = "skip-expansion", action = ArgAction::SetFalse)]
    pub must_expand_regions: bool,

    #[arg(long = "early-exit", action = ArgAction::SetTrue)]
    pub must_attempt_early_exit: bool,

    #[arg(long = "dry-run", action = ArgAction::SetTrue)]
    pub is_dry_run: bool,

    #[arg(long = "no-color", action = ArgAction::SetTrue, help = "disable output coloring")]
    pub is_color_disabled: bool,
}

//
// pub struct ModeArg
//

#[derive(Debug, Clone, Args)]
#[group(required = true, multiple = false)]
pub struct ModeArg {
    #[arg(short, long, value_name = "TITLE_TIER")]
    pub title_tier: Option<TitleTier>,

    #[arg(short, long, value_name = "REGIONS_MAP_FILE")]
    pub regions_map: Option<PathBuf>,
}

//
// Service
//

fn validate_mod_prefix(prefix_str: &str) -> Result<String, &'static str> {
    const EMPTY_PREFIX_ERROR:   &'static str = "mod prefix must not be empty";
    const INVALID_PREFIX_ERROR: &'static str = "mod prefix must contain only alphanumeric characters and underscores";

    if prefix_str.is_empty() {
        Err(EMPTY_PREFIX_ERROR)
    } else if config::is_valid_mod_prefix(prefix_str) {
        Ok(String::from(prefix_str))
    } else {
        Err(INVALID_PREFIX_ERROR)
    }
}