1use std::path::PathBuf;
2
3use clap::{Args, Parser, Subcommand};
4
5#[derive(Debug, Parser)]
6#[command(about = "A cashu mint written in rust", author = env!("CARGO_PKG_AUTHORS"), version = env!("CARGO_PKG_VERSION"))]
7pub struct CLIArgs {
8 #[arg(
9 short,
10 long,
11 help = "Use the <directory> as the location of the database",
12 required = false
13 )]
14 pub work_dir: Option<PathBuf>,
15 #[cfg(feature = "sqlcipher")]
16 #[arg(
17 short,
18 long,
19 global = true,
20 help = "Database password for SQLCipher (required when opening an encrypted database)"
21 )]
22 pub password: Option<String>,
23 #[arg(
24 short,
25 long,
26 global = true,
27 help = "Legacy startup flag; use `config init` or `config apply` instead",
28 required = false
29 )]
30 pub config: Option<PathBuf>,
31 #[arg(
32 long,
33 global = true,
34 help = "Legacy seed file; accepted only by `config migrate`",
35 required = false
36 )]
37 pub seed_file: Option<PathBuf>,
38 #[arg(
39 long,
40 help = "Enable logging output",
41 required = false,
42 action = clap::ArgAction::SetTrue,
43 default_value = "true"
44 )]
45 pub enable_logging: bool,
46 #[command(subcommand)]
47 pub command: Option<Commands>,
48}
49
50#[derive(Debug, Subcommand)]
52pub enum Commands {
53 Config(ConfigArgs),
55}
56
57#[derive(Debug, Args)]
59pub struct ConfigArgs {
60 #[command(subcommand)]
61 pub command: ConfigCommands,
62}
63
64#[derive(Debug, Subcommand)]
66pub enum ConfigCommands {
67 Migrate(MigrateConfigArgs),
69 Init(InitConfigArgs),
71 Validate(ConfigFileArgs),
73 Apply(ApplyConfigArgs),
75 Rollback,
77 Show,
79 Export(ExportConfigArgs),
81}
82
83#[derive(Debug, Args)]
85pub struct InitConfigArgs {
86 #[arg(long)]
88 pub file: PathBuf,
89 #[arg(
91 long,
92 required_unless_present = "existing_mint",
93 conflicts_with = "existing_mint"
94 )]
95 pub new_mint: bool,
96 #[arg(
98 long,
99 required_unless_present = "new_mint",
100 conflicts_with = "new_mint"
101 )]
102 pub existing_mint: bool,
103 #[arg(long, requires = "existing_mint", conflicts_with = "new_mint")]
105 pub allow_new_bdk_wallet: bool,
106}
107
108#[derive(Debug, Args)]
110pub struct MigrateConfigArgs {
111 #[arg(long)]
113 pub file: PathBuf,
114 #[arg(long)]
116 pub output: PathBuf,
117 #[arg(long)]
119 pub secrets_dir: Option<PathBuf>,
120 #[arg(long)]
122 pub force: bool,
123}
124
125#[derive(Debug, Args)]
127pub struct ConfigFileArgs {
128 #[arg(long)]
130 pub file: PathBuf,
131}
132
133#[derive(Debug, Args)]
135pub struct ExportConfigArgs {
136 #[arg(long)]
138 pub file: PathBuf,
139 #[arg(long)]
141 pub force: bool,
142}
143
144#[derive(Debug, Args)]
146pub struct ApplyConfigArgs {
147 #[arg(long)]
149 pub file: PathBuf,
150 #[arg(long)]
152 pub validate_only: bool,
153 #[arg(long)]
155 pub allow_new_bdk_wallet: bool,
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn parses_configuration_commands() {
164 CLIArgs::try_parse_from([
165 "cdk-mintd",
166 "config",
167 "init",
168 "--new-mint",
169 "--file",
170 "/tmp/mint.toml",
171 ])
172 .expect("new-mint initialization should parse");
173 CLIArgs::try_parse_from([
174 "cdk-mintd",
175 "config",
176 "init",
177 "--existing-mint",
178 "--file",
179 "/tmp/mint.toml",
180 ])
181 .expect("existing-mint initialization should parse");
182 CLIArgs::try_parse_from([
183 "cdk-mintd",
184 "config",
185 "validate",
186 "--file",
187 "/tmp/mint.toml",
188 ])
189 .expect("configuration validation should parse");
190
191 let args =
192 CLIArgs::try_parse_from(["cdk-mintd", "config", "export", "--file", "/tmp/mint.toml"])
193 .expect("configuration export should parse");
194 assert!(matches!(
195 args.command,
196 Some(Commands::Config(ConfigArgs {
197 command: ConfigCommands::Export(ExportConfigArgs { force: false, .. }),
198 }))
199 ));
200
201 let args = CLIArgs::try_parse_from([
202 "cdk-mintd",
203 "config",
204 "export",
205 "--file",
206 "/tmp/mint.toml",
207 "--force",
208 ])
209 .expect("configuration export should parse");
210 assert!(matches!(
211 args.command,
212 Some(Commands::Config(ConfigArgs {
213 command: ConfigCommands::Export(ExportConfigArgs { force: true, .. }),
214 }))
215 ));
216
217 CLIArgs::try_parse_from([
218 "cdk-mintd",
219 "config",
220 "apply",
221 "--file",
222 "/tmp/mint.toml",
223 "--validate-only",
224 ])
225 .expect("configuration apply should parse");
226 CLIArgs::try_parse_from(["cdk-mintd", "config", "show"])
227 .expect("configuration show should parse");
228 CLIArgs::try_parse_from(["cdk-mintd", "config", "rollback"])
229 .expect("configuration rollback should parse");
230
231 let args = CLIArgs::try_parse_from([
232 "cdk-mintd",
233 "config",
234 "migrate",
235 "--file",
236 "/tmp/legacy.toml",
237 "--output",
238 "/tmp/migrated.toml",
239 "--secrets-dir",
240 "/tmp/mint-secrets",
241 ])
242 .expect("configuration migration should parse");
243 assert!(matches!(
244 args.command,
245 Some(Commands::Config(ConfigArgs {
246 command: ConfigCommands::Migrate(MigrateConfigArgs { force: false, .. }),
247 }))
248 ));
249
250 let args = CLIArgs::try_parse_from([
251 "cdk-mintd",
252 "--seed-file",
253 "/tmp/seed.txt",
254 "config",
255 "migrate",
256 "--file",
257 "/tmp/legacy.toml",
258 "--output",
259 "/tmp/migrated.toml",
260 ])
261 .expect("legacy seed-file migration should parse");
262 assert_eq!(args.seed_file, Some(PathBuf::from("/tmp/seed.txt")));
263 }
264
265 #[test]
266 fn initialization_requires_exactly_one_mint_mode() {
267 assert!(CLIArgs::try_parse_from([
268 "cdk-mintd",
269 "config",
270 "init",
271 "--file",
272 "/tmp/mint.toml",
273 ])
274 .is_err());
275 assert!(CLIArgs::try_parse_from([
276 "cdk-mintd",
277 "config",
278 "init",
279 "--new-mint",
280 "--existing-mint",
281 "--file",
282 "/tmp/mint.toml",
283 ])
284 .is_err());
285 assert!(CLIArgs::try_parse_from([
286 "cdk-mintd",
287 "config",
288 "init",
289 "--new-mint",
290 "--allow-new-bdk-wallet",
291 "--file",
292 "/tmp/mint.toml",
293 ])
294 .is_err());
295
296 let args = CLIArgs::try_parse_from([
297 "cdk-mintd",
298 "config",
299 "init",
300 "--existing-mint",
301 "--allow-new-bdk-wallet",
302 "--file",
303 "/tmp/mint.toml",
304 ])
305 .expect("existing mint may explicitly allow a new BDK wallet");
306 assert!(matches!(
307 args.command,
308 Some(Commands::Config(ConfigArgs {
309 command: ConfigCommands::Init(InitConfigArgs {
310 allow_new_bdk_wallet: true,
311 ..
312 }),
313 }))
314 ));
315 }
316
317 #[test]
318 fn no_subcommand_still_parses_daemon_startup() {
319 let args = CLIArgs::try_parse_from(["cdk-mintd"]).expect("daemon arguments should parse");
320 assert!(args.command.is_none());
321 }
322}