Improve argument parsing (#452)

Fixes #440
This commit is contained in:
Denis Isidoro 2021-01-07 09:31:34 -03:00 committed by GitHub
parent c1a2bd8d30
commit 5c43c5173a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 134 additions and 102 deletions

99
Cargo.lock generated
View file

@ -9,15 +9,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.32"
@ -84,19 +75,36 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "clap"
version = "2.33.3"
version = "3.0.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"clap_derive",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "clap_derive"
version = "3.0.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.40",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@ -168,6 +176,12 @@ dependencies = [
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
[[package]]
name = "heck"
version = "0.3.1"
@ -186,6 +200,16 @@ dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -206,9 +230,10 @@ checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "navi"
version = "2.13.1"
version = "2.14.0"
dependencies = [
"anyhow",
"clap",
"directories-next",
"edit",
"lazy_static",
@ -216,7 +241,6 @@ dependencies = [
"regex",
"shellwords",
"strip-ansi-escapes",
"structopt",
"terminal_size",
"termion",
"thiserror",
@ -229,6 +253,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "os_str_bytes"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
[[package]]
name = "ppv-lite86"
version = "0.2.10"
@ -465,33 +495,9 @@ dependencies = [
[[package]]
name = "strsim"
version = "0.8.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cc388d94ffabf39b5ed5fadddc40147cb21e605f53db6f8f36a625d27489ac5"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e2513111825077552a6751dfad9e11ce0fba07d7276a3943a037d7e93e64c5f"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.40",
]
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
@ -529,6 +535,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.13"
@ -553,9 +568,9 @@ dependencies = [
[[package]]
name = "textwrap"
version = "0.11.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
dependencies = [
"unicode-width",
]

View file

@ -1,6 +1,6 @@
[package]
name = "navi"
version = "2.13.1"
version = "2.14.0"
authors = ["Denis Isidoro <denis_isidoro@live.com>"]
edition = "2018"
description = "An interactive cheatsheet tool for the command-line"
@ -16,7 +16,7 @@ travis-ci = { repository = "denisidoro/navi", branch = "master" }
[dependencies]
regex = "1.3.9"
structopt = "0.3.17"
clap = "3.0.0-beta.2"
termion = "1.5.5"
raw_tty = "0.1.0"
lazy_static = "1.4.0"

View file

@ -25,7 +25,7 @@ pub fn all_cheat_files(path_str: &str) -> Vec<String> {
.collect::<Vec<String>>()
}
fn paths_from_path_param<'a>(env_var: &'a str) -> impl Iterator<Item = &'a str> + 'a {
fn paths_from_path_param(env_var: &str) -> impl Iterator<Item = &str> {
env_var.split(':').filter(|folder| folder != &"")
}

View file

@ -42,7 +42,7 @@ fn parse_opts(text: &str) -> Result<FinderOpts, Error> {
})
.collect::<Vec<_>>()
.chunks(2)
.map(|flag_and_value| {
.try_for_each(|flag_and_value| {
if let [flag, value] = flag_and_value {
match flag.as_str() {
"--headers" | "--header-lines" => {
@ -74,7 +74,6 @@ fn parse_opts(text: &str) -> Result<FinderOpts, Error> {
unreachable!() // Chunking by 2 allows only for tuples of 1 or 2 items...
}
})
.collect::<Result<_, _>>()
.context("Failed to parse finder options")?;
let suggestion_type = match (multi, prevent_extra) {

View file

@ -3,44 +3,61 @@ use crate::cmds::info::Info;
use crate::common::shell::Shell;
use crate::env_vars;
use crate::finder::FinderChoice;
use anyhow::Error;
use structopt::{clap::AppSettings, StructOpt};
use clap::{crate_version, AppSettings, Clap};
use std::str::FromStr;
static mut NOTIFIED_DEPRECATION: bool = false;
fn parse_finder(src: &str) -> Result<FinderChoice, Error> {
match src.to_lowercase().as_str() {
"fzf" => Ok(FinderChoice::Fzf),
"skim" => Ok(FinderChoice::Skim),
_ => Err(Error::msg(format!("unknown finder '{}'", src))),
impl FromStr for FinderChoice {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"fzf" => Ok(FinderChoice::Fzf),
"skim" => Ok(FinderChoice::Skim),
_ => Err("no match"),
}
}
}
fn parse_shell(src: &str) -> Result<Shell, Error> {
match src.to_lowercase().as_str() {
"bash" => Ok(Shell::Bash),
"zsh" => Ok(Shell::Zsh),
"fish" => Ok(Shell::Fish),
_ => Err(Error::msg(format!("unknown shell '{}'", src))),
impl FromStr for Shell {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bash" => Ok(Shell::Bash),
"zsh" => Ok(Shell::Zsh),
"fish" => Ok(Shell::Fish),
_ => Err("no match"),
}
}
}
fn parse_func(src: &str) -> Result<Func, Error> {
match src.to_lowercase().as_str() {
"url::open" => Ok(Func::UrlOpen),
"welcome" => Ok(Func::Welcome),
_ => Err(Error::msg(format!("unknown shell '{}'", src))),
impl FromStr for Func {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"url::open" => Ok(Func::UrlOpen),
"welcome" => Ok(Func::Welcome),
_ => Err("no match"),
}
}
}
fn parse_info(src: &str) -> Result<Info, Error> {
match src.to_lowercase().as_str() {
"cheats-path" => Ok(Info::CheatsPath),
_ => Err(Error::msg(format!("unknown info '{}'", src))),
impl FromStr for Info {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"cheats-path" => Ok(Info::CheatsPath),
_ => Err("no match"),
}
}
}
#[derive(Debug, StructOpt)]
#[structopt(after_help = r#"MORE INFO:
#[derive(Debug, Clap)]
#[clap(after_help = r#"MORE INFO:
Please refer to https://github.com/denisidoro/navi
EXAMPLES:
@ -59,72 +76,73 @@ EXAMPLES:
navi --fzf-overrides '--no-select-1' # prevent autoselection in case of single line
navi --fzf-overrides '--nth 1,2' # only consider the first two columns for search
navi --fzf-overrides '--no-exact' # use looser search algorithm"#)]
#[structopt(setting = AppSettings::ColorAuto)]
#[structopt(setting = AppSettings::ColoredHelp)]
#[structopt(setting = AppSettings::AllowLeadingHyphen)]
#[clap(setting = AppSettings::ColorAuto)]
#[clap(setting = AppSettings::ColoredHelp)]
#[clap(setting = AppSettings::AllowLeadingHyphen)]
#[clap(version = crate_version!())]
pub struct Config {
/// List of :-separated paths containing .cheat files
#[structopt(short, long, env = env_vars::PATH)]
#[clap(short, long, env = env_vars::PATH)]
pub path: Option<String>,
/// [Experimental] Instead of executing a snippet, saves it to a file
#[structopt(short, long)]
#[clap(short, long)]
save: Option<String>,
/// Instead of executing a snippet, prints it to stdout
#[structopt(long)]
#[clap(long)]
print: bool,
/// Prevents autoselection in case of single entry
#[structopt(long)]
#[clap(long)]
no_autoselect: bool,
/// Hides preview window
#[structopt(long)]
#[clap(long)]
pub no_preview: bool,
/// Returns the best match
#[structopt(long)]
#[clap(long)]
best_match: bool,
/// Search for cheatsheets using the tldr-pages repository
#[structopt(long)]
#[clap(long)]
tldr: Option<String>,
/// Search for cheatsheets using the cheat.sh repository
#[structopt(long)]
#[clap(long)]
cheatsh: Option<String>,
/// Query
#[structopt(short, long)]
#[clap(short, long)]
query: Option<String>,
/// finder overrides for cheat selection
#[structopt(long, env = env_vars::FZF_OVERRIDES)]
#[clap(long, env = env_vars::FZF_OVERRIDES)]
pub fzf_overrides: Option<String>,
/// finder overrides for variable selection
#[structopt(long, env = env_vars::FZF_OVERRIDES_VAR)]
#[clap(long, env = env_vars::FZF_OVERRIDES_VAR)]
pub fzf_overrides_var: Option<String>,
/// which finder application to use
#[structopt(long, env = env_vars::FINDER, default_value = "fzf", parse(try_from_str = parse_finder))]
#[clap(long, env = env_vars::FINDER, default_value = "fzf", possible_values = &["fzf", "skim"], case_insensitive = true)]
pub finder: FinderChoice,
#[structopt(subcommand)]
#[clap(subcommand)]
pub cmd: Option<Command>,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Clap)]
pub enum Command {
/// Filters results
#[structopt(setting = AppSettings::Hidden)]
#[clap(setting = AppSettings::Hidden)]
Query {
/// String used as filter (example: "git")
query: String,
},
/// Autoselects the snippet that best matches the query
#[structopt(setting = AppSettings::Hidden)]
#[clap(setting = AppSettings::Hidden)]
Best {
/// String used as filter (example: "git remove branch")
query: String,
@ -134,24 +152,24 @@ pub enum Command {
/// Performs ad-hoc functions provided by navi
Fn {
/// Function name (example: "url::open")
#[structopt(parse(try_from_str = parse_func))]
#[clap(possible_values = &["url::welcome", "open"], case_insensitive = true)]
func: Func,
/// List of arguments (example: "https://google.com")
args: Vec<String>,
},
/// Manages cheatsheet repositories
Repo {
#[structopt(subcommand)]
#[clap(subcommand)]
cmd: RepoCommand,
},
/// Used for fzf's preview window when selecting snippets
#[structopt(setting = AppSettings::Hidden)]
#[clap(setting = AppSettings::Hidden)]
Preview {
/// Selection line
line: String,
},
/// Used for fzf's preview window when selecting variable suggestions
#[structopt(setting = AppSettings::Hidden)]
#[clap(setting = AppSettings::Hidden)]
PreviewVar {
/// Selection line
selection: String,
@ -162,23 +180,23 @@ pub enum Command {
},
/// Shows the path for shell widget files
Widget {
#[structopt(default_value = "bash", parse(try_from_str = parse_shell))]
#[clap(possible_values = &["bash", "zsh", "fish"], case_insensitive = true, default_value = "bash")]
shell: Shell,
},
/// Shows info
Info {
#[structopt(parse(try_from_str = parse_info))]
#[clap(possible_values = &["cheats-path"], case_insensitive = true)]
info: Info,
},
/// Helper command for Alfred integration
#[structopt(setting = AppSettings::Hidden)]
#[clap(setting = AppSettings::Hidden)]
Alfred {
#[structopt(subcommand)]
#[clap(subcommand)]
cmd: AlfredCommand,
},
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Clap)]
pub enum RepoCommand {
/// Imports cheatsheets from a repo
Add {
@ -189,7 +207,7 @@ pub enum RepoCommand {
Browse,
}
#[derive(Debug, StructOpt)]
#[derive(Debug, Clap)]
pub enum AlfredCommand {
/// Outputs a JSON with commands
Start,
@ -301,9 +319,9 @@ impl Config {
}
pub fn config_from_env() -> Config {
Config::from_args()
Config::parse()
}
pub fn config_from_iter(args: Vec<&str>) -> Config {
Config::from_iter(args)
Config::parse_from(args)
}