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

0xmichalis / nftbk / 18681775225

21 Oct 2025 11:05AM UTC coverage: 39.734% (+2.1%) from 37.593%
18681775225

push

github

0xmichalis
feat: move cli code into cli modules and use subcommands

81 of 423 new or added lines in 7 files covered. (19.15%)

1823 of 4588 relevant lines covered (39.73%)

7.32 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

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

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

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

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

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

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

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

46
        /// Exit on the first error encountered
47
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
48
        exit_on_error: bool,
49

50
        /// Path to a TOML file with IPFS provider configuration
51
        #[arg(long)]
52
        ipfs_config: Option<String>,
53
    },
54
    /// Server-related operations
55
    Server {
56
        #[command(subcommand)]
57
        command: ServerCommands,
58
    },
59
}
60

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

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

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

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

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

85
        /// Path to a TOML file with IPFS provider configuration
86
        #[arg(long)]
87
        ipfs_config: Option<String>,
88

89
        /// Request server to pin downloaded assets on IPFS
90
        #[arg(long, default_value_t = false, action = clap::ArgAction::Set)]
91
        pin_on_ipfs: bool,
92
    },
93
    /// List existing backups on the server
94
    List {
95
        /// The server address to request backups from
96
        #[arg(long, default_value = "http://127.0.0.1:8080")]
97
        server_address: String,
98
    },
99
}
100

101
impl Cli {
NEW
102
    pub async fn run(self) -> Result<()> {
×
NEW
103
        match self.command {
×
104
            Commands::Create {
NEW
105
                chains_config_path,
×
NEW
106
                tokens_config_path,
×
NEW
107
                output_path,
×
NEW
108
                prune_redundant,
×
NEW
109
                exit_on_error,
×
NEW
110
                ipfs_config,
×
111
            } => {
112
                commands::create::run(
NEW
113
                    chains_config_path,
×
NEW
114
                    tokens_config_path,
×
NEW
115
                    output_path,
×
NEW
116
                    prune_redundant,
×
NEW
117
                    exit_on_error,
×
NEW
118
                    ipfs_config,
×
119
                )
NEW
120
                .await
×
121
            }
NEW
122
            Commands::Server { command } => match command {
×
123
                ServerCommands::Create {
NEW
124
                    tokens_config_path,
×
NEW
125
                    server_address,
×
NEW
126
                    output_path,
×
NEW
127
                    force,
×
NEW
128
                    user_agent,
×
NEW
129
                    ipfs_config,
×
NEW
130
                    pin_on_ipfs,
×
131
                } => {
132
                    commands::server::create::run(
NEW
133
                        tokens_config_path,
×
NEW
134
                        server_address,
×
NEW
135
                        output_path,
×
NEW
136
                        force,
×
NEW
137
                        user_agent,
×
NEW
138
                        ipfs_config,
×
NEW
139
                        pin_on_ipfs,
×
140
                    )
NEW
141
                    .await
×
142
                }
NEW
143
                ServerCommands::List { server_address } => {
×
NEW
144
                    commands::server::list::run(server_address).await
×
145
                }
146
            },
147
        }
148
    }
149
}
150

151
#[cfg(test)]
152
mod tests {
153
    use super::*;
154
    use clap::Parser;
155

156
    mod cli_parsing_tests {
157
        use super::*;
158

159
        #[test]
160
        fn parses_create_command_with_defaults() {
161
            let args = vec!["nftbk-cli", "create"];
162
            let cli = Cli::try_parse_from(args).unwrap();
163

164
            assert_eq!(cli.log_level, LogLevel::Info);
165
            assert!(!cli.no_color);
166
            match cli.command {
167
                Commands::Create {
168
                    chains_config_path,
169
                    tokens_config_path,
170
                    output_path,
171
                    prune_redundant,
172
                    exit_on_error,
173
                    ipfs_config,
174
                } => {
175
                    assert_eq!(chains_config_path, PathBuf::from("config_chains.toml"));
176
                    assert_eq!(tokens_config_path, PathBuf::from("config_tokens.toml"));
177
                    assert_eq!(output_path, Some(PathBuf::from("nft_backup")));
178
                    assert!(!prune_redundant);
179
                    assert!(!exit_on_error);
180
                    assert!(ipfs_config.is_none());
181
                }
182
                _ => panic!("Expected Create command"),
183
            }
184
        }
185

186
        #[test]
187
        fn parses_create_command_with_custom_options() {
188
            let args = vec![
189
                "nftbk-cli",
190
                "--log-level",
191
                "debug",
192
                "--no-color",
193
                "true",
194
                "create",
195
                "--chains-config-path",
196
                "custom_chains.toml",
197
                "--tokens-config-path",
198
                "custom_tokens.toml",
199
                "--output-path",
200
                "/tmp/backup",
201
                "--prune-redundant",
202
                "true",
203
                "--exit-on-error",
204
                "true",
205
                "--ipfs-config",
206
                "ipfs.toml",
207
            ];
208
            let cli = Cli::try_parse_from(args).unwrap();
209

210
            assert_eq!(cli.log_level, LogLevel::Debug);
211
            assert!(cli.no_color);
212
            match cli.command {
213
                Commands::Create {
214
                    chains_config_path,
215
                    tokens_config_path,
216
                    output_path,
217
                    prune_redundant,
218
                    exit_on_error,
219
                    ipfs_config,
220
                } => {
221
                    assert_eq!(chains_config_path, PathBuf::from("custom_chains.toml"));
222
                    assert_eq!(tokens_config_path, PathBuf::from("custom_tokens.toml"));
223
                    assert_eq!(output_path, Some(PathBuf::from("/tmp/backup")));
224
                    assert!(prune_redundant);
225
                    assert!(exit_on_error);
226
                    assert_eq!(ipfs_config, Some("ipfs.toml".to_string()));
227
                }
228
                _ => panic!("Expected Create command"),
229
            }
230
        }
231

232
        #[test]
233
        fn parses_server_create_command_with_defaults() {
234
            let args = vec!["nftbk-cli", "server", "create"];
235
            let cli = Cli::try_parse_from(args).unwrap();
236

237
            match cli.command {
238
                Commands::Server { command } => match command {
239
                    ServerCommands::Create {
240
                        tokens_config_path,
241
                        server_address,
242
                        output_path,
243
                        force,
244
                        user_agent,
245
                        ipfs_config,
246
                        pin_on_ipfs,
247
                    } => {
248
                        assert_eq!(tokens_config_path, PathBuf::from("config_tokens.toml"));
249
                        assert_eq!(server_address, "http://127.0.0.1:8080");
250
                        assert_eq!(output_path, Some(PathBuf::from("nft_backup")));
251
                        assert!(!force);
252
                        assert_eq!(user_agent, "Linux");
253
                        assert!(ipfs_config.is_none());
254
                        assert!(!pin_on_ipfs);
255
                    }
256
                    _ => panic!("Expected Server Create command"),
257
                },
258
                _ => panic!("Expected Server command"),
259
            }
260
        }
261

262
        #[test]
263
        fn parses_server_create_command_with_custom_options() {
264
            let args = vec![
265
                "nftbk-cli",
266
                "server",
267
                "create",
268
                "--tokens-config-path",
269
                "custom_tokens.toml",
270
                "--server-address",
271
                "https://api.example.com",
272
                "--output-path",
273
                "/tmp/server_backup",
274
                "--force",
275
                "true",
276
                "--user-agent",
277
                "CustomAgent/1.0",
278
                "--ipfs-config",
279
                "ipfs.toml",
280
                "--pin-on-ipfs",
281
                "true",
282
            ];
283
            let cli = Cli::try_parse_from(args).unwrap();
284

285
            match cli.command {
286
                Commands::Server { command } => match command {
287
                    ServerCommands::Create {
288
                        tokens_config_path,
289
                        server_address,
290
                        output_path,
291
                        force,
292
                        user_agent,
293
                        ipfs_config,
294
                        pin_on_ipfs,
295
                    } => {
296
                        assert_eq!(tokens_config_path, PathBuf::from("custom_tokens.toml"));
297
                        assert_eq!(server_address, "https://api.example.com");
298
                        assert_eq!(output_path, Some(PathBuf::from("/tmp/server_backup")));
299
                        assert!(force);
300
                        assert_eq!(user_agent, "CustomAgent/1.0");
301
                        assert_eq!(ipfs_config, Some("ipfs.toml".to_string()));
302
                        assert!(pin_on_ipfs);
303
                    }
304
                    _ => panic!("Expected Server Create command"),
305
                },
306
                _ => panic!("Expected Server command"),
307
            }
308
        }
309

310
        #[test]
311
        fn parses_server_list_command_with_defaults() {
312
            let args = vec!["nftbk-cli", "server", "list"];
313
            let cli = Cli::try_parse_from(args).unwrap();
314

315
            match cli.command {
316
                Commands::Server { command } => match command {
317
                    ServerCommands::List { server_address } => {
318
                        assert_eq!(server_address, "http://127.0.0.1:8080");
319
                    }
320
                    _ => panic!("Expected Server List command"),
321
                },
322
                _ => panic!("Expected Server command"),
323
            }
324
        }
325

326
        #[test]
327
        fn parses_server_list_command_with_custom_server() {
328
            let args = vec![
329
                "nftbk-cli",
330
                "server",
331
                "list",
332
                "--server-address",
333
                "https://api.example.com",
334
            ];
335
            let cli = Cli::try_parse_from(args).unwrap();
336

337
            match cli.command {
338
                Commands::Server { command } => match command {
339
                    ServerCommands::List { server_address } => {
340
                        assert_eq!(server_address, "https://api.example.com");
341
                    }
342
                    _ => panic!("Expected Server List command"),
343
                },
344
                _ => panic!("Expected Server command"),
345
            }
346
        }
347

348
        #[test]
349
        fn parses_all_log_levels() {
350
            for (level_str, expected_level) in [
351
                ("debug", LogLevel::Debug),
352
                ("info", LogLevel::Info),
353
                ("warn", LogLevel::Warn),
354
                ("error", LogLevel::Error),
355
            ] {
356
                let args = vec!["nftbk-cli", "--log-level", level_str, "create"];
357
                let cli = Cli::try_parse_from(args).unwrap();
358
                assert_eq!(cli.log_level, expected_level);
359
            }
360
        }
361

362
        #[test]
363
        fn handles_no_color_flag() {
364
            let args = vec!["nftbk-cli", "--no-color", "true", "create"];
365
            let cli = Cli::try_parse_from(args).unwrap();
366
            assert!(cli.no_color);
367

368
            let args = vec!["nftbk-cli", "--no-color", "false", "create"];
369
            let cli = Cli::try_parse_from(args).unwrap();
370
            assert!(!cli.no_color);
371
        }
372

373
        #[test]
374
        fn requires_subcommand() {
375
            let args = vec!["nftbk-cli"];
376
            let result = Cli::try_parse_from(args);
377
            assert!(result.is_err());
378
        }
379

380
        #[test]
381
        fn validates_log_level_enum() {
382
            let args = vec!["nftbk-cli", "--log-level", "invalid", "create"];
383
            let result = Cli::try_parse_from(args);
384
            assert!(result.is_err());
385
        }
386
    }
387
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc