Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use derive interface of clap #110

Merged
merged 3 commits into from
Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ version = "0.18"
default-features = false

[dependencies]
clap = { version = "4", features = ["cargo", "wrap_help"] }
clap = { version = "4", features = ["cargo", "wrap_help", "derive"] }
clap_complete = "4"
clap_complete_nushell = "4"
slog = "2.5"
Expand Down
150 changes: 56 additions & 94 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,101 +1,63 @@
#[macro_use]
extern crate clap;

#[macro_use]
extern crate slog;

use clap::ArgAction;
use clap::{CommandFactory, Parser as _};
use clap_complete::{generate, Shell};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is commandfactory necessary here? most of the flag-only examples don't seem to require it

Copy link
Contributor Author

@ErichDonGubler ErichDonGubler Apr 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's so we can call Cli::command to feed a Command structure to clap_completion::generate below. If one removes the import, they get the following compiler error (assuming a compilation based on a4d7c46):

error[E0599]: no function or associated item named `command` found for struct `Cli` in the current scope
  --> src/main.rs:53:35
   |
12 | struct Cli {
   | ---------- function or associated item `command` not found for this struct
...
53 |         let mut args_clone = Cli::command();
   |                                   ^^^^^^^ function or associated item not found in `Cli`
   |
   = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
   |
1  + use clap::CommandFactory;
   |

For more information about this error, try `rustc --explain E0599`.
error: could not compile `git-absorb` (bin "git-absorb") due to 1 previous error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consider this resolved, but I'll leave this open for visibility.

use clap_complete_nushell::Nushell;
use slog::Drain;
use std::io;

/// Automatically absorb staged changes into your current branch
#[derive(Debug, clap::Parser)]
struct Cli {
/// Use this commit as the base of the absorb stack
#[clap(long, short)]
base: Option<String>,
/// Don't make any actual changes
#[clap(long, short = 'n')]
dry_run: bool,
/// Skip safety checks
#[clap(long, short)]
force: bool,
/// Display more output
#[clap(long, short)]
verbose: bool,
/// Run rebase if successful
#[clap(long, short = 'r')]
and_rebase: bool,
/// Generate completions
#[clap(long, value_parser = ["bash", "fish", "nushell", "zsh", "powershell", "elvish"])]
gen_completions: Option<String>,
/// Match the change against the complete file
#[clap(long, short)]
whole_file: bool,
/// Only generate one fixup per commit
#[clap(long, short = 'F')]
one_fixup_per_commit: bool,
}

fn main() {
let args = command!()
.about("Automatically absorb staged changes into your current branch")
.arg(
clap::Arg::new("base")
.help("Use this commit as the base of the absorb stack")
.short('b')
.long("base"),
)
.arg(
clap::Arg::new("dry-run")
.help("Don't make any actual changes")
.short('n')
.long("dry-run")
.action(ArgAction::SetTrue),
)
.arg(
clap::Arg::new("force")
.help("Skip safety checks")
.short('f')
.long("force")
.action(ArgAction::SetTrue),
)
.arg(
clap::Arg::new("verbose")
.help("Display more output")
.short('v')
.long("verbose")
.action(ArgAction::SetTrue),
)
.arg(
clap::Arg::new("and-rebase")
.help("Run rebase if successful")
.short('r')
.long("and-rebase")
.action(ArgAction::SetTrue),
)
.arg(
clap::Arg::new("gen-completions")
.help("Generate completions")
.long("gen-completions")
.value_parser(["bash", "fish", "nushell", "zsh", "powershell", "elvish"]),
)
.arg(
clap::Arg::new("whole-file")
.help("Match the change against the complete file ")
.short('w')
.long("whole-file")
.action(ArgAction::SetTrue),
)
.arg(
clap::Arg::new("one-fixup-per-commit")
.help("Only generate one fixup per commit")
.short('F')
.long("one-fixup-per-commit")
.action(ArgAction::SetTrue),
);
let mut args_clone = args.clone();
let args = args.get_matches();
let Cli {
base,
dry_run,
force,
verbose,
and_rebase,
gen_completions,
whole_file,
one_fixup_per_commit,
} = Cli::parse();

if let Some(shell) = args.get_one::<String>("gen-completions") {
if let Some(shell) = gen_completions {
let app_name = "git-absorb";
let mut cmd = Cli::command();
match shell.as_str() {
"bash" => {
generate(Shell::Bash, &mut args_clone, app_name, &mut io::stdout());
}
"fish" => {
generate(Shell::Fish, &mut args_clone, app_name, &mut io::stdout());
}
"nushell" => {
generate(Nushell, &mut args_clone, app_name, &mut io::stdout());
}
"zsh" => {
generate(Shell::Zsh, &mut args_clone, app_name, &mut io::stdout());
}
"powershell" => {
generate(
Shell::PowerShell,
&mut args_clone,
app_name,
&mut io::stdout(),
);
}
"elvish" => {
generate(Shell::Elvish, &mut args_clone, app_name, &mut io::stdout());
}
"bash" => generate(Shell::Bash, &mut cmd, app_name, &mut io::stdout()),
"fish" => generate(Shell::Fish, &mut cmd, app_name, &mut io::stdout()),
"nushell" => generate(Nushell, &mut cmd, app_name, &mut io::stdout()),
"zsh" => generate(Shell::Zsh, &mut cmd, app_name, &mut io::stdout()),
"powershell" => generate(Shell::PowerShell, &mut cmd, app_name, &mut io::stdout()),
"elvish" => generate(Shell::Elvish, &mut cmd, app_name, &mut io::stdout()),
_ => unreachable!(),
}
return;
Expand All @@ -106,28 +68,28 @@ fn main() {
let drain = slog_async::Async::new(drain).build().fuse();
let drain = slog::LevelFilter::new(
drain,
if args.get_flag("verbose") {
if verbose {
slog::Level::Debug
} else {
slog::Level::Info
},
)
.fuse();
let mut logger = slog::Logger::root(drain, o!());
if args.get_flag("verbose") {
if verbose {
logger = logger.new(o!(
"module" => slog::FnValue(|record| record.module()),
"line" => slog::FnValue(|record| record.line()),
));
}

if let Err(e) = git_absorb::run(&mut git_absorb::Config {
dry_run: args.get_flag("dry-run"),
force: args.get_flag("force"),
base: args.get_one::<String>("base").map(|s| s.as_str()),
and_rebase: args.get_flag("and-rebase"),
whole_file: args.get_flag("whole-file"),
one_fixup_per_commit: args.get_flag("one-fixup-per-commit"),
dry_run,
force,
base: base.as_deref(),
and_rebase,
whole_file,
one_fixup_per_commit,
logger: &logger,
}) {
crit!(logger, "absorb failed"; "err" => e.to_string());
Expand Down
Loading