• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

0xmichalis / nftbk / 18688951784

21 Oct 2025 03:22PM UTC coverage: 44.836% (+0.1%) from 44.698%
18688951784

push

github

0xmichalis
refactor: unify various configs

13 of 47 new or added lines in 6 files covered. (27.66%)

2 existing lines in 2 files now uncovered.

2110 of 4706 relevant lines covered (44.84%)

7.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/cli/mod.rs
1
use std::path::PathBuf;
2

3
use anyhow::Result;
4
use clap::{Parser, Subcommand};
5

6
use crate::logging::LogLevel;
7

8
pub mod commands;
9
pub mod config;
10
pub mod x402;
11

12
#[derive(Parser, Debug)]
13
#[command(author, version, about, long_about = None)]
14
pub struct Cli {
15
    /// Set the log level
16
    #[arg(short, long, value_enum, default_value = "info")]
17
    pub log_level: LogLevel,
18

19
    /// Disable colored log output
20
    #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
21
    pub no_color: bool,
22

23
    #[command(subcommand)]
24
    pub command: Commands,
25
}
26

27
#[derive(Subcommand, Debug)]
28
pub enum Commands {
29
    /// Create a backup locally
30
    Create {
31
        /// The path to the unified configuration file
32
        #[arg(short = 'c', long, default_value = "config.toml")]
33
        config_path: PathBuf,
34

35
        /// The path to the tokens configuration file
36
        #[arg(short = 't', long, default_value = "config_tokens.toml")]
37
        tokens_config_path: PathBuf,
38

39
        /// The directory to save the backup to
40
        #[arg(short, long, default_value = "nft_backup")]
41
        output_path: Option<PathBuf>,
42

43
        /// Delete redundant files in the backup folder
44
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
45
        prune_redundant: bool,
46

47
        /// Exit on the first error encountered
48
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
49
        exit_on_error: bool,
50
    },
51
    /// Server-related operations
52
    Server {
53
        #[command(subcommand)]
54
        command: ServerCommands,
55
    },
56
}
57

58
#[derive(Subcommand, Debug)]
59
pub enum ServerCommands {
60
    /// Create a backup on the server
61
    Create {
62
        /// The path to the tokens configuration file
63
        #[arg(short = 't', long, default_value = "config_tokens.toml")]
64
        tokens_config_path: PathBuf,
65

66
        /// The server address to request backups from
67
        #[arg(long, default_value = "http://127.0.0.1:8080")]
68
        server_address: String,
69

70
        /// The directory to save the backup to
71
        #[arg(short, long, default_value = "nft_backup")]
72
        output_path: Option<PathBuf>,
73

74
        /// Force rerunning a completed backup task
75
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
76
        force: bool,
77

78
        /// User-Agent to send to the server (affects archive format)
79
        #[arg(long, default_value = "Linux")]
80
        user_agent: String,
81

82
        /// Request server to pin downloaded assets on IPFS
83
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
84
        pin_on_ipfs: bool,
85

86
        /// Polling interval in milliseconds for checking backup status
87
        #[arg(long, default_value = "10000")]
88
        polling_interval_ms: u64,
89
    },
90
    /// List existing backups on the server
91
    List {
92
        /// The server address to request backups from
93
        #[arg(long, default_value = "http://127.0.0.1:8080")]
94
        server_address: String,
95

96
        /// Show error details in the output table
97
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
98
        show_errors: bool,
99
    },
100
    /// Delete a backup from the server
101
    Delete {
102
        /// The server address to request backups from
103
        #[arg(long, default_value = "http://127.0.0.1:8080")]
104
        server_address: String,
105

106
        /// The task ID of the backup to delete
107
        #[arg(short = 't', long)]
108
        task_id: String,
109

110
        /// Show what would be deleted without actually deleting
111
        #[arg(long, default_value_t = true, action = clap::ArgAction::Set)]
112
        dry_run: bool,
113
    },
114
}
115

116
impl Cli {
117
    pub async fn run(self) -> Result<()> {
×
118
        match self.command {
×
119
            Commands::Create {
NEW
120
                config_path,
×
121
                tokens_config_path,
×
122
                output_path,
×
123
                prune_redundant,
×
124
                exit_on_error,
×
125
            } => {
126
                commands::create::run(
NEW
127
                    config_path,
×
128
                    tokens_config_path,
×
129
                    output_path,
×
130
                    prune_redundant,
×
131
                    exit_on_error,
×
132
                )
UNCOV
133
                .await
×
134
            }
135
            Commands::Server { command } => match command {
×
136
                ServerCommands::Create {
137
                    tokens_config_path,
×
138
                    server_address,
×
139
                    output_path,
×
140
                    force,
×
141
                    user_agent,
×
142
                    pin_on_ipfs,
×
143
                    polling_interval_ms,
×
144
                } => {
145
                    commands::server::create::run(
146
                        tokens_config_path,
×
147
                        server_address,
×
148
                        output_path,
×
149
                        force,
×
150
                        user_agent,
×
151
                        pin_on_ipfs,
×
152
                        Some(polling_interval_ms),
×
153
                    )
154
                    .await
×
155
                }
156
                ServerCommands::List {
157
                    server_address,
×
158
                    show_errors,
×
159
                } => commands::server::list::run(server_address, show_errors).await,
×
160
                ServerCommands::Delete {
161
                    server_address,
×
162
                    task_id,
×
163
                    dry_run,
×
164
                } => commands::server::delete::run(server_address, task_id, dry_run).await,
×
165
            },
166
        }
167
    }
168
}
169

170
#[cfg(test)]
171
mod tests {
172
    use super::*;
173
    use clap::Parser;
174

175
    mod cli_parsing_tests {
176
        use super::*;
177

178
        #[test]
179
        fn parses_create_command_with_defaults() {
180
            let args = vec!["nftbk-cli", "create"];
181
            let cli = Cli::try_parse_from(args).unwrap();
182

183
            assert_eq!(cli.log_level, LogLevel::Info);
184
            assert!(!cli.no_color);
185
            match cli.command {
186
                Commands::Create {
187
                    config_path,
188
                    tokens_config_path,
189
                    output_path,
190
                    prune_redundant,
191
                    exit_on_error,
192
                } => {
193
                    assert_eq!(config_path, PathBuf::from("config.toml"));
194
                    assert_eq!(tokens_config_path, PathBuf::from("config_tokens.toml"));
195
                    assert_eq!(output_path, Some(PathBuf::from("nft_backup")));
196
                    assert!(!prune_redundant);
197
                    assert!(!exit_on_error);
198
                }
199
                _ => panic!("Expected Create command"),
200
            }
201
        }
202

203
        #[test]
204
        fn parses_create_command_with_custom_options() {
205
            let args = vec![
206
                "nftbk-cli",
207
                "--log-level",
208
                "debug",
209
                "--no-color",
210
                "true",
211
                "create",
212
                "--config-path",
213
                "custom_config.toml",
214
                "--tokens-config-path",
215
                "custom_tokens.toml",
216
                "--output-path",
217
                "/tmp/backup",
218
                "--prune-redundant",
219
                "true",
220
                "--exit-on-error",
221
                "true",
222
            ];
223
            let cli = Cli::try_parse_from(args).unwrap();
224

225
            assert_eq!(cli.log_level, LogLevel::Debug);
226
            assert!(cli.no_color);
227
            match cli.command {
228
                Commands::Create {
229
                    config_path,
230
                    tokens_config_path,
231
                    output_path,
232
                    prune_redundant,
233
                    exit_on_error,
234
                } => {
235
                    assert_eq!(config_path, PathBuf::from("custom_config.toml"));
236
                    assert_eq!(tokens_config_path, PathBuf::from("custom_tokens.toml"));
237
                    assert_eq!(output_path, Some(PathBuf::from("/tmp/backup")));
238
                    assert!(prune_redundant);
239
                    assert!(exit_on_error);
240
                }
241
                _ => panic!("Expected Create command"),
242
            }
243
        }
244

245
        #[test]
246
        fn parses_server_create_command_with_defaults() {
247
            let args = vec!["nftbk-cli", "server", "create"];
248
            let cli = Cli::try_parse_from(args).unwrap();
249

250
            match cli.command {
251
                Commands::Server { command } => match command {
252
                    ServerCommands::Create {
253
                        tokens_config_path,
254
                        server_address,
255
                        output_path,
256
                        force,
257
                        user_agent,
258
                        pin_on_ipfs,
259
                        polling_interval_ms,
260
                    } => {
261
                        assert_eq!(tokens_config_path, PathBuf::from("config_tokens.toml"));
262
                        assert_eq!(server_address, "http://127.0.0.1:8080");
263
                        assert_eq!(output_path, Some(PathBuf::from("nft_backup")));
264
                        assert!(!force);
265
                        assert_eq!(user_agent, "Linux");
266
                        assert!(!pin_on_ipfs);
267
                        assert_eq!(polling_interval_ms, 10000);
268
                    }
269
                    _ => panic!("Expected Server Create command"),
270
                },
271
                _ => panic!("Expected Server command"),
272
            }
273
        }
274

275
        #[test]
276
        fn parses_server_create_command_with_custom_options() {
277
            let args = vec![
278
                "nftbk-cli",
279
                "server",
280
                "create",
281
                "--tokens-config-path",
282
                "custom_tokens.toml",
283
                "--server-address",
284
                "https://api.example.com",
285
                "--output-path",
286
                "/tmp/server_backup",
287
                "--force",
288
                "true",
289
                "--user-agent",
290
                "CustomAgent/1.0",
291
                "--pin-on-ipfs",
292
                "true",
293
            ];
294
            let cli = Cli::try_parse_from(args).unwrap();
295

296
            match cli.command {
297
                Commands::Server { command } => match command {
298
                    ServerCommands::Create {
299
                        tokens_config_path,
300
                        server_address,
301
                        output_path,
302
                        force,
303
                        user_agent,
304
                        pin_on_ipfs,
305
                        polling_interval_ms,
306
                    } => {
307
                        assert_eq!(tokens_config_path, PathBuf::from("custom_tokens.toml"));
308
                        assert_eq!(server_address, "https://api.example.com");
309
                        assert_eq!(output_path, Some(PathBuf::from("/tmp/server_backup")));
310
                        assert!(force);
311
                        assert_eq!(user_agent, "CustomAgent/1.0");
312
                        assert!(pin_on_ipfs);
313
                        assert_eq!(polling_interval_ms, 10000); // Default value
314
                    }
315
                    _ => panic!("Expected Server Create command"),
316
                },
317
                _ => panic!("Expected Server command"),
318
            }
319
        }
320

321
        #[test]
322
        fn parses_server_list_command_with_defaults() {
323
            let args = vec!["nftbk-cli", "server", "list"];
324
            let cli = Cli::try_parse_from(args).unwrap();
325

326
            match cli.command {
327
                Commands::Server { command } => match command {
328
                    ServerCommands::List {
329
                        server_address,
330
                        show_errors,
331
                    } => {
332
                        assert_eq!(server_address, "http://127.0.0.1:8080");
333
                        assert!(!show_errors);
334
                    }
335
                    _ => panic!("Expected Server List command"),
336
                },
337
                _ => panic!("Expected Server command"),
338
            }
339
        }
340

341
        #[test]
342
        fn parses_server_list_command_with_custom_server() {
343
            let args = vec![
344
                "nftbk-cli",
345
                "server",
346
                "list",
347
                "--server-address",
348
                "https://api.example.com",
349
            ];
350
            let cli = Cli::try_parse_from(args).unwrap();
351

352
            match cli.command {
353
                Commands::Server { command } => match command {
354
                    ServerCommands::List {
355
                        server_address,
356
                        show_errors,
357
                    } => {
358
                        assert_eq!(server_address, "https://api.example.com");
359
                        assert!(!show_errors);
360
                    }
361
                    _ => panic!("Expected Server List command"),
362
                },
363
                _ => panic!("Expected Server command"),
364
            }
365
        }
366

367
        #[test]
368
        fn parses_server_list_command_with_show_errors() {
369
            let args = vec!["nftbk-cli", "server", "list", "--show-errors", "true"];
370
            let cli = Cli::try_parse_from(args).unwrap();
371

372
            match cli.command {
373
                Commands::Server { command } => match command {
374
                    ServerCommands::List {
375
                        server_address,
376
                        show_errors,
377
                    } => {
378
                        assert_eq!(server_address, "http://127.0.0.1:8080");
379
                        assert!(show_errors);
380
                    }
381
                    _ => panic!("Expected Server List command"),
382
                },
383
                _ => panic!("Expected Server command"),
384
            }
385
        }
386

387
        #[test]
388
        fn parses_server_delete_command_with_defaults() {
389
            let args = vec![
390
                "nftbk-cli",
391
                "server",
392
                "delete",
393
                "--task-id",
394
                "test-task-123",
395
            ];
396
            let cli = Cli::try_parse_from(args).unwrap();
397

398
            match cli.command {
399
                Commands::Server { command } => match command {
400
                    ServerCommands::Delete {
401
                        server_address,
402
                        task_id,
403
                        dry_run,
404
                    } => {
405
                        assert_eq!(server_address, "http://127.0.0.1:8080");
406
                        assert_eq!(task_id, "test-task-123");
407
                        assert!(dry_run);
408
                    }
409
                    _ => panic!("Expected Server Delete command"),
410
                },
411
                _ => panic!("Expected Server command"),
412
            }
413
        }
414

415
        #[test]
416
        fn parses_server_delete_command_with_custom_options() {
417
            let args = vec![
418
                "nftbk-cli",
419
                "server",
420
                "delete",
421
                "--server-address",
422
                "https://api.example.com",
423
                "--task-id",
424
                "custom-task-456",
425
                "--dry-run",
426
                "true",
427
            ];
428
            let cli = Cli::try_parse_from(args).unwrap();
429

430
            match cli.command {
431
                Commands::Server { command } => match command {
432
                    ServerCommands::Delete {
433
                        server_address,
434
                        task_id,
435
                        dry_run,
436
                    } => {
437
                        assert_eq!(server_address, "https://api.example.com");
438
                        assert_eq!(task_id, "custom-task-456");
439
                        assert!(dry_run);
440
                    }
441
                    _ => panic!("Expected Server Delete command"),
442
                },
443
                _ => panic!("Expected Server command"),
444
            }
445
        }
446

447
        #[test]
448
        fn parses_all_log_levels() {
449
            for (level_str, expected_level) in [
450
                ("debug", LogLevel::Debug),
451
                ("info", LogLevel::Info),
452
                ("warn", LogLevel::Warn),
453
                ("error", LogLevel::Error),
454
            ] {
455
                let args = vec!["nftbk-cli", "--log-level", level_str, "create"];
456
                let cli = Cli::try_parse_from(args).unwrap();
457
                assert_eq!(cli.log_level, expected_level);
458
            }
459
        }
460

461
        #[test]
462
        fn handles_no_color_flag() {
463
            let args = vec!["nftbk-cli", "--no-color", "true", "create"];
464
            let cli = Cli::try_parse_from(args).unwrap();
465
            assert!(cli.no_color);
466

467
            let args = vec!["nftbk-cli", "--no-color", "false", "create"];
468
            let cli = Cli::try_parse_from(args).unwrap();
469
            assert!(!cli.no_color);
470
        }
471

472
        #[test]
473
        fn requires_subcommand() {
474
            let args = vec!["nftbk-cli"];
475
            let result = Cli::try_parse_from(args);
476
            assert!(result.is_err());
477
        }
478

479
        #[test]
480
        fn validates_log_level_enum() {
481
            let args = vec!["nftbk-cli", "--log-level", "invalid", "create"];
482
            let result = Cli::try_parse_from(args);
483
            assert!(result.is_err());
484
        }
485
    }
486
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc