1use std::path::PathBuf;
2
3use clap::{ArgAction, Args, Parser, Subcommand};
4
5#[derive(Debug, Parser)]
6#[command(
7 name = "numi",
8 version,
9 about = "Generate Swift code from Apple project resources",
10 long_about = "Generate Swift code from asset catalogs, localization files, and other project resources.",
11 before_help = "Generate Swift code from Apple project resources",
12 after_help = "Examples:\n numi init\n numi generate\n numi check\n numi generate --workspace\n numi dump-context --job l10n",
13 propagate_version = true,
14 subcommand_required = true
15)]
16pub struct Cli {
17 #[command(subcommand)]
18 pub command: Option<Command>,
19}
20
21#[derive(Debug, Subcommand)]
22pub enum Command {
23 #[command(
24 about = "Generate outputs for one config or workspace",
25 after_help = "Examples:\n numi generate\n numi generate --job assets --job l10n\n numi generate --workspace"
26 )]
27 Generate(GenerateArgs),
28 #[command(
29 about = "Check whether generated outputs are up to date",
30 after_help = "Examples:\n numi check\n numi check --job l10n\n numi check --workspace"
31 )]
32 Check(CheckArgs),
33 #[command(about = "Write a starter numi.toml in the current directory")]
34 Init(InitArgs),
35 #[command(about = "Inspect resolved config paths and values")]
36 Config(ConfigCommand),
37 #[command(
38 name = "dump-context",
39 about = "Print the template context for a single job",
40 after_help = "Examples:\n numi dump-context --job l10n\n numi dump-context --config AppUI/numi.toml --job assets"
41 )]
42 DumpContext(DumpContextArgs),
43}
44
45#[derive(Debug, Args)]
46#[command(about = "Inspect resolved config paths and values")]
47pub struct ConfigCommand {
48 #[command(subcommand)]
49 pub command: ConfigSubcommand,
50}
51
52#[derive(Debug, Subcommand)]
53pub enum ConfigSubcommand {
54 #[command(about = "Print the resolved config path")]
55 Locate(LocateArgs),
56 #[command(about = "Print the resolved config with defaults applied")]
57 Print(PrintArgs),
58}
59
60#[derive(Debug, Args)]
61pub struct LocateArgs {
62 #[arg(
63 long = "config",
64 help = "Use a specific numi.toml instead of auto-discovery"
65 )]
66 pub config: Option<PathBuf>,
67}
68
69#[derive(Debug, Args)]
70pub struct GenerateArgs {
71 #[arg(
72 long = "config",
73 help = "Use a specific numi.toml instead of auto-discovery"
74 )]
75 pub config: Option<PathBuf>,
76 #[arg(
77 long = "workspace",
78 action = ArgAction::SetTrue,
79 help = "Use the ancestor workspace manifest instead of the nearest member manifest"
80 )]
81 pub workspace: bool,
82 #[arg(long = "job", help = "Limit generation to the selected job name")]
83 pub jobs: Vec<String>,
84 #[command(flatten)]
85 pub incremental_override: IncrementalOverrideArgs,
86}
87
88#[derive(Debug, Args)]
89pub struct CheckArgs {
90 #[arg(
91 long = "config",
92 help = "Use a specific numi.toml instead of auto-discovery"
93 )]
94 pub config: Option<PathBuf>,
95 #[arg(
96 long = "workspace",
97 action = ArgAction::SetTrue,
98 help = "Use the ancestor workspace manifest instead of the nearest member manifest"
99 )]
100 pub workspace: bool,
101 #[arg(long = "job", help = "Limit checking to the selected job name")]
102 pub jobs: Vec<String>,
103}
104
105#[derive(Debug, Args)]
106pub struct InitArgs {
107 #[arg(
108 long,
109 help = "Overwrite an existing numi.toml in the current directory"
110 )]
111 pub force: bool,
112}
113
114#[derive(Debug, Args)]
115pub struct PrintArgs {
116 #[arg(
117 long = "config",
118 help = "Use a specific numi.toml instead of auto-discovery"
119 )]
120 pub config: Option<PathBuf>,
121}
122
123#[derive(Debug, Args)]
124pub struct DumpContextArgs {
125 #[arg(
126 long = "config",
127 help = "Use a specific numi.toml instead of auto-discovery"
128 )]
129 pub config: Option<PathBuf>,
130 #[arg(long = "job", help = "Job name to render as JSON context")]
131 pub job: String,
132}
133
134#[derive(Debug, Args, Default, Clone, PartialEq, Eq)]
135pub struct IncrementalOverrideArgs {
136 #[arg(
137 long = "incremental",
138 action = ArgAction::SetTrue,
139 help = "Force incremental parsing when supported",
140 conflicts_with = "no_incremental"
141 )]
142 pub incremental: bool,
143 #[arg(
144 long = "no-incremental",
145 action = ArgAction::SetTrue,
146 help = "Disable incremental parsing even when the config enables it"
147 )]
148 pub no_incremental: bool,
149}
150
151impl IncrementalOverrideArgs {
152 pub fn resolve(&self) -> Option<bool> {
153 if self.incremental {
154 Some(true)
155 } else if self.no_incremental {
156 Some(false)
157 } else {
158 None
159 }
160 }
161}