Skip to content

Commit

Permalink
feat(complete): Completions with derive
Browse files Browse the repository at this point in the history
Trait `Completable` allows calling `complete` on items deriving Parser.
  • Loading branch information
ModProg committed Oct 7, 2022
1 parent d820175 commit 6b4827e
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 30 deletions.
48 changes: 19 additions & 29 deletions clap_complete/examples/dynamic.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,25 @@
use clap::FromArgMatches;
use clap::Subcommand;
use std::path::PathBuf;

fn command() -> clap::Command {
let cmd = clap::Command::new("dynamic")
.arg(
clap::Arg::new("input")
.long("input")
.short('i')
.value_hint(clap::ValueHint::FilePath),
)
.arg(
clap::Arg::new("format")
.long("format")
.short('F')
.value_parser(["json", "yaml", "toml"]),
)
.args_conflicts_with_subcommands(true);
clap_complete::dynamic::CompleteCommand::augment_subcommands(cmd)
use clap::Parser;
use clap::ValueEnum;
use clap::ValueHint;
use clap_complete::dynamic::Completeable;

#[derive(Parser, Debug)]
struct Opts {
#[arg(long, short, value_hint = ValueHint::FilePath)]
input: PathBuf,
#[arg(long, short)]
format: Format,
}

fn main() {
let cmd = command();
let matches = cmd.get_matches();
if let Ok(completions) = clap_complete::dynamic::CompleteCommand::from_arg_matches(&matches) {
completions.complete(&mut command());
} else {
println!("{:#?}", matches);
}
#[derive(ValueEnum, Clone, Debug)]
enum Format {
Json,
Yaml,
Toml,
}

#[test]
fn verify_cli() {
command().debug_assert();
fn main() {
println!("{:#?}", Opts::complete_or_parse());
}
74 changes: 73 additions & 1 deletion clap_complete/src/dynamic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use std::{
path::{self, Path, PathBuf},
};

use clap::{builder::StyledStr, Arg, Args, Command, Parser, Subcommand, ValueEnum};
use clap::{
builder::StyledStr, Arg, Args, Command, CommandFactory, Error, Parser, Subcommand, ValueEnum,
};
use clap_lex::{ArgCursor, RawArgs, RawOsStr, RawOsString};

use bash::Bash;
Expand Down Expand Up @@ -60,6 +62,76 @@ impl CompleteCommand {
}
}

/// Trait completing an App
///
/// Allows to be used like this on an item deriving [`Parser`]
///
/// **NOTE:** This parses any invocation with the subcommand `complete` present making this
/// conflict with any command expecting `complete` as a valid argument or subcommand.
///
/// ```
/// use std::path::PathBuf;
///
/// use clap::Parser;
/// use clap_complete::dynamic::Completeable;
///
/// #[derive(Parser)]
/// struct Opts {
/// file: Option<PathBuf>
/// }
///
/// fn main() {
/// Opts::complete_or_parse();
/// }
/// ```
pub trait Completeable {
/// Either trigger dynamic completion or parse arguments as T
///
/// This is a drop-in replacement for [`Parser::parse()`].
fn complete_or_parse() -> Self
where
Self: Parser,
{
Self::complete();
Self::parse()
}
/// Either trigger dynamic completion or parse try to parse arguments as T
///
/// This is a drop-in replacement for [`Parser::try_parse()`].
fn try_complete_or_parse() -> Result<Self, Error>
where
Self: Parser,
{
Self::try_complete()?;
Self::try_parse()
}
/// Trigger dynamic completion if the `complete` subcommand is present do nothing if not
///
/// Exits on failing completions
fn complete()
where
Self: CommandFactory,
{
if let Ok(c) = CompleteCommand::try_parse() {
c.complete(&mut Self::command());
}
}
/// Trigger dynamic completion if the `complete` subcommand is present do nothing if not
///
/// Errors, if completions fail for any reason.
fn try_complete() -> Result<(), Error>
where
Self: CommandFactory,
{
if let Ok(c) = CompleteCommand::try_parse() {
c.try_complete(&mut Self::command())?;
}
Ok(())
}
}

impl<T: CommandFactory> Completeable for T {}

#[derive(Subcommand, Clone, Debug)]
#[command(hide = true)]
#[allow(missing_docs)]
Expand Down

0 comments on commit 6b4827e

Please sign in to comment.