use std::path::PathBuf;
use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
#[derive(Debug, Parser)]
#[command(
name = "numi",
version,
about = "Generate Swift code from Apple project resources",
long_about = "Generate Swift code from asset catalogs, localization files, and other project resources.",
before_help = "Generate Swift code from Apple project resources",
after_help = "Examples:\n numi init\n numi generate\n numi check\n numi generate --workspace\n numi dump-context --job l10n",
propagate_version = true,
subcommand_required = true
)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Command>,
}
#[derive(Debug, Subcommand)]
pub enum Command {
#[command(
about = "Generate outputs for one config or workspace",
after_help = "Examples:\n numi generate\n numi generate --job assets --job l10n\n numi generate --workspace"
)]
Generate(GenerateArgs),
#[command(
about = "Check whether generated outputs are up to date",
after_help = "Examples:\n numi check\n numi check --job l10n\n numi check --workspace"
)]
Check(CheckArgs),
#[command(about = "Write a starter numi.toml in the current directory")]
Init(InitArgs),
#[command(about = "Inspect resolved config paths and values")]
Config(ConfigCommand),
#[command(
name = "dump-context",
about = "Print the template context for a single job",
after_help = "Examples:\n numi dump-context --job l10n\n numi dump-context --config AppUI/numi.toml --job assets"
)]
DumpContext(DumpContextArgs),
}
#[derive(Debug, Args)]
#[command(about = "Inspect resolved config paths and values")]
pub struct ConfigCommand {
#[command(subcommand)]
pub command: ConfigSubcommand,
}
#[derive(Debug, Subcommand)]
pub enum ConfigSubcommand {
#[command(about = "Print the resolved config path")]
Locate(LocateArgs),
#[command(about = "Print the resolved config with defaults applied")]
Print(PrintArgs),
}
#[derive(Debug, Args)]
pub struct LocateArgs {
#[arg(
long = "config",
help = "Use a specific numi.toml instead of auto-discovery"
)]
pub config: Option<PathBuf>,
}
#[derive(Debug, Args)]
pub struct GenerateArgs {
#[arg(
long = "config",
help = "Use a specific numi.toml instead of auto-discovery"
)]
pub config: Option<PathBuf>,
#[arg(
long = "workspace",
action = ArgAction::SetTrue,
help = "Use the ancestor workspace manifest instead of the nearest member manifest"
)]
pub workspace: bool,
#[arg(long = "job", help = "Limit generation to the selected job name")]
pub jobs: Vec<String>,
#[command(flatten)]
pub incremental_override: IncrementalOverrideArgs,
}
#[derive(Debug, Args)]
pub struct CheckArgs {
#[arg(
long = "config",
help = "Use a specific numi.toml instead of auto-discovery"
)]
pub config: Option<PathBuf>,
#[arg(
long = "workspace",
action = ArgAction::SetTrue,
help = "Use the ancestor workspace manifest instead of the nearest member manifest"
)]
pub workspace: bool,
#[arg(long = "job", help = "Limit checking to the selected job name")]
pub jobs: Vec<String>,
}
#[derive(Debug, Args)]
pub struct InitArgs {
#[arg(
long,
help = "Overwrite an existing numi.toml in the current directory"
)]
pub force: bool,
}
#[derive(Debug, Args)]
pub struct PrintArgs {
#[arg(
long = "config",
help = "Use a specific numi.toml instead of auto-discovery"
)]
pub config: Option<PathBuf>,
}
#[derive(Debug, Args)]
pub struct DumpContextArgs {
#[arg(
long = "config",
help = "Use a specific numi.toml instead of auto-discovery"
)]
pub config: Option<PathBuf>,
#[arg(long = "job", help = "Job name to render as JSON context")]
pub job: String,
}
#[derive(Debug, Args, Default, Clone, PartialEq, Eq)]
pub struct IncrementalOverrideArgs {
#[arg(
long = "incremental",
value_enum,
value_name = "MODE",
help = "Control generation cache behavior for this run"
)]
pub incremental: Option<IncrementalMode>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum IncrementalMode {
Auto,
Always,
Never,
Refresh,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct IncrementalResolution {
pub incremental: Option<bool>,
pub parse_cache: Option<bool>,
pub force_regenerate: bool,
}
impl IncrementalOverrideArgs {
pub fn resolve(&self) -> IncrementalResolution {
match self.incremental {
Some(IncrementalMode::Auto) | None => IncrementalResolution::default(),
Some(IncrementalMode::Always) => IncrementalResolution {
incremental: Some(true),
parse_cache: Some(true),
force_regenerate: false,
},
Some(IncrementalMode::Never) => IncrementalResolution {
incremental: Some(false),
parse_cache: Some(false),
force_regenerate: false,
},
Some(IncrementalMode::Refresh) => IncrementalResolution {
incremental: Some(true),
parse_cache: Some(true),
force_regenerate: true,
},
}
}
}