Skip to content

Commit 9c77012

Browse files
committed
feat(cmd): use clap derive for commands
1 parent 0516ab0 commit 9c77012

15 files changed

Lines changed: 299 additions & 672 deletions

File tree

Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ readme = './README.md'
1818
[dependencies]
1919
async-trait = "0.1.89"
2020
tokio = { version = "1.48.0", features = ["full"] }
21-
clap = { version = "4.5.51", features = ["cargo"] }
21+
clap = { version = "4.5.51", features = ["cargo", "derive"] }
2222
colored = "3.0.0"
2323
dirs = "6.0.0"
2424
env_logger = "0.11.6"

src/cli.rs

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
//! Clap Commanders
22
use crate::{
33
cmd::{
4-
completion_handler, Command, CompletionCommand, DataCommand, EditCommand, ExecCommand,
5-
ListCommand, PickCommand, StatCommand, TestCommand,
4+
CompletionsArgs, DataArgs, EditArgs, ExecArgs, ListArgs, PickArgs, StatArgs, TestArgs,
65
},
76
err::Error,
8-
flag::{Debug, Flag},
97
};
10-
use clap::crate_version;
8+
use clap::{CommandFactory, Parser, Subcommand};
9+
use env_logger::Env;
1110
use log::LevelFilter;
1211

1312
/// This should be called before calling any cli method or printing any output.
@@ -23,46 +22,81 @@ pub fn reset_signal_pipe_handler() {
2322
}
2423
}
2524

25+
/// May the Code be with You
26+
#[derive(Parser)]
27+
#[command(name = "leetcode", version, about = "May the Code be with You 👻")]
28+
#[command(arg_required_else_help = true)]
29+
pub struct Cli {
30+
/// Debug mode
31+
#[arg(short, long)]
32+
pub debug: bool,
33+
34+
#[command(subcommand)]
35+
pub command: Option<Commands>,
36+
}
37+
38+
#[derive(Subcommand)]
39+
pub enum Commands {
40+
/// Manage Cache
41+
#[command(visible_alias = "d", display_order = 1)]
42+
Data(DataArgs),
43+
44+
/// Edit question
45+
#[command(visible_alias = "e", display_order = 2)]
46+
Edit(EditArgs),
47+
48+
/// Submit solution
49+
#[command(visible_alias = "x", display_order = 3)]
50+
Exec(ExecArgs),
51+
52+
/// List problems
53+
#[command(visible_alias = "l", display_order = 4)]
54+
List(ListArgs),
55+
56+
/// Pick a problem
57+
#[command(visible_alias = "p", display_order = 5)]
58+
Pick(PickArgs),
59+
60+
/// Show simple chart about submissions
61+
#[command(visible_alias = "s", display_order = 6)]
62+
Stat(StatArgs),
63+
64+
/// Test a question
65+
#[command(visible_alias = "t", display_order = 7)]
66+
Test(TestArgs),
67+
68+
/// Generate shell Completions
69+
#[command(visible_alias = "c", display_order = 8)]
70+
Completions(CompletionsArgs),
71+
}
72+
2673
/// Get matches
2774
pub async fn main() -> Result<(), Error> {
2875
reset_signal_pipe_handler();
2976

30-
let mut cmd = clap::Command::new("leetcode")
31-
.version(crate_version!())
32-
.about("May the Code be with You 👻")
33-
.subcommands(vec![
34-
DataCommand::usage().display_order(1),
35-
EditCommand::usage().display_order(2),
36-
ExecCommand::usage().display_order(3),
37-
ListCommand::usage().display_order(4),
38-
PickCommand::usage().display_order(5),
39-
StatCommand::usage().display_order(6),
40-
TestCommand::usage().display_order(7),
41-
CompletionCommand::usage().display_order(8),
42-
])
43-
.arg(Debug::usage())
44-
.arg_required_else_help(true);
45-
46-
let m = cmd.clone().get_matches();
47-
48-
if m.get_flag("debug") {
49-
Debug::handler()?;
77+
let cli = Cli::parse();
78+
79+
if cli.debug {
80+
env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init();
5081
} else {
5182
env_logger::Builder::new()
5283
.filter_level(LevelFilter::Info)
5384
.format_timestamp(None)
5485
.init();
5586
}
5687

57-
match m.subcommand() {
58-
Some(("data", sub_m)) => Ok(DataCommand::handler(sub_m).await?),
59-
Some(("edit", sub_m)) => Ok(EditCommand::handler(sub_m).await?),
60-
Some(("exec", sub_m)) => Ok(ExecCommand::handler(sub_m).await?),
61-
Some(("list", sub_m)) => Ok(ListCommand::handler(sub_m).await?),
62-
Some(("pick", sub_m)) => Ok(PickCommand::handler(sub_m).await?),
63-
Some(("stat", sub_m)) => Ok(StatCommand::handler(sub_m).await?),
64-
Some(("test", sub_m)) => Ok(TestCommand::handler(sub_m).await?),
65-
Some(("completions", sub_m)) => Ok(completion_handler(sub_m, &mut cmd)?),
66-
_ => Err(Error::MatchError),
88+
match cli.command {
89+
Some(Commands::Data(args)) => args.run().await,
90+
Some(Commands::Edit(args)) => args.run().await,
91+
Some(Commands::Exec(args)) => args.run().await,
92+
Some(Commands::List(args)) => args.run().await,
93+
Some(Commands::Pick(args)) => args.run().await,
94+
Some(Commands::Stat(args)) => args.run().await,
95+
Some(Commands::Test(args)) => args.run().await,
96+
Some(Commands::Completions(args)) => {
97+
let mut cmd = Cli::command();
98+
args.run(&mut cmd)
99+
}
100+
None => Err(Error::MatchError),
67101
}
68102
}

src/cmd/completions.rs

Lines changed: 18 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,14 @@
11
//! Completions command
2-
3-
use super::Command;
42
use crate::err::Error;
5-
use async_trait::async_trait;
6-
use clap::{Arg, ArgAction, ArgMatches, Command as ClapCommand};
3+
use clap::{Args, Command as ClapCommand};
74
use clap_complete::{Generator, Shell, generate};
85

9-
/// Abstract shell completions command
10-
///
11-
/// ```sh
12-
/// Generate shell Completions
13-
///
14-
/// USAGE:
15-
/// leetcode completions <shell>
16-
///
17-
/// ARGUMENTS:
18-
/// <shell> [possible values: bash, elvish, fish, powershell, zsh]
19-
/// ```
20-
pub struct CompletionCommand;
21-
22-
#[async_trait]
23-
impl Command for CompletionCommand {
24-
/// `pick` usage
25-
fn usage() -> ClapCommand {
26-
ClapCommand::new("completions")
27-
.about("Generate shell Completions")
28-
.visible_alias("c")
29-
.arg(
30-
Arg::new("shell")
31-
.action(ArgAction::Set)
32-
.value_parser(clap::value_parser!(Shell)),
33-
)
34-
}
35-
36-
async fn handler(_m: &ArgMatches) -> Result<(), Error> {
37-
// defining custom handler to print the completions. Handler method signature limits taking
38-
// other params. We need &ArgMatches and &mut ClapCommand to generate completions.
39-
println!(
40-
"Don't use this handler. Does not implement the functionality to print completions. Use completions_handler() below."
41-
);
42-
Ok(())
43-
}
6+
/// Completions command arguments
7+
#[derive(Args)]
8+
pub struct CompletionsArgs {
9+
/// Shell type [possible values: bash, elvish, fish, powershell, zsh]
10+
#[arg(value_parser = clap::value_parser!(Shell))]
11+
pub shell: Option<Shell>,
4412
}
4513

4614
fn get_completions_string<G: Generator>(
@@ -53,12 +21,15 @@ fn get_completions_string<G: Generator>(
5321
Ok(String::from_utf8(v)?)
5422
}
5523

56-
pub fn completion_handler(m: &ArgMatches, cmd: &mut ClapCommand) -> Result<(), Error> {
57-
let shell = *m.get_one::<Shell>("shell").unwrap_or(
58-
// if shell value is not provided try to get from the environment
59-
&Shell::from_env().ok_or(Error::MatchError)?,
60-
);
61-
let completions = get_completions_string(shell, cmd)?;
62-
println!("{}", completions);
63-
Ok(())
24+
impl CompletionsArgs {
25+
/// Generate and print shell completions
26+
pub fn run(&self, cmd: &mut ClapCommand) -> Result<(), Error> {
27+
let shell = self
28+
.shell
29+
.or_else(Shell::from_env)
30+
.ok_or(Error::MatchError)?;
31+
let completions = get_completions_string(shell, cmd)?;
32+
println!("{}", completions);
33+
Ok(())
34+
}
6435
}

src/cmd/data.rs

Lines changed: 16 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,23 @@
1-
//! Cache managger
2-
use super::Command;
1+
//! Cache manager
32
use crate::{cache::Cache, helper::Digit, Error};
4-
use async_trait::async_trait;
5-
use clap::{Arg, ArgAction, ArgMatches, Command as ClapCommand};
3+
use clap::Args;
64
use colored::Colorize;
75

8-
/// Abstract `data` command
9-
///
10-
/// ```sh
11-
/// leetcode-data
12-
/// Manage Cache
13-
///
14-
/// USAGE:
15-
/// leetcode data [FLAGS]
16-
///
17-
/// FLAGS:
18-
/// -d, --delete Delete cache
19-
/// -u, --update Update cache
20-
/// -h, --help Prints help information
21-
/// -V, --version Prints version information
22-
/// ```
23-
pub struct DataCommand;
6+
/// Data command arguments
7+
#[derive(Args)]
8+
pub struct DataArgs {
9+
/// Delete cache
10+
#[arg(short, long)]
11+
pub delete: bool,
2412

25-
#[async_trait]
26-
impl Command for DataCommand {
27-
/// `data` command usage
28-
fn usage() -> ClapCommand {
29-
ClapCommand::new("data")
30-
.about("Manage Cache")
31-
.visible_alias("d")
32-
.arg(
33-
Arg::new("delete")
34-
.display_order(1)
35-
.short('d')
36-
.long("delete")
37-
.help("Delete cache")
38-
.action(ArgAction::SetTrue),
39-
)
40-
.arg(
41-
Arg::new("update")
42-
.display_order(2)
43-
.short('u')
44-
.long("update")
45-
.help("Update cache")
46-
.action(ArgAction::SetTrue),
47-
)
48-
}
13+
/// Update cache
14+
#[arg(short, long)]
15+
pub update: bool,
16+
}
4917

18+
impl DataArgs {
5019
/// `data` handler
51-
async fn handler(m: &ArgMatches) -> Result<(), Error> {
20+
pub async fn run(&self) -> Result<(), Error> {
5221
use std::fs::File;
5322
use std::path::Path;
5423

@@ -75,13 +44,13 @@ impl Command for DataCommand {
7544
title.push_str(&"-".repeat(65));
7645

7746
let mut flags = 0;
78-
if m.get_flag("delete") {
47+
if self.delete {
7948
flags += 1;
8049
cache.clean()?;
8150
println!("{}", "ok!".bright_green());
8251
}
8352

84-
if m.get_flag("update") {
53+
if self.update {
8554
flags += 1;
8655
cache.update().await?;
8756
println!("{}", "ok!".bright_green());

0 commit comments

Comments
 (0)