Skip to content

Commit

Permalink
Merge pull request #349 from pacak/rc-0.9.10
Browse files Browse the repository at this point in the history
Release 0.9.10
  • Loading branch information
pacak authored Mar 19, 2024
2 parents b7c0b16 + 26fc2bc commit 9088f7f
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 29 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bpaf"
version = "0.9.9"
version = "0.9.10"
edition = "2021"
categories = ["command-line-interface"]
description = "A simple Command Line Argument Parser with parser combinators"
Expand All @@ -19,7 +19,7 @@ include = [


[dependencies]
bpaf_derive = { path = "./bpaf_derive", version = "=0.5.7", optional = true }
bpaf_derive = { path = "./bpaf_derive", version = "=0.5.10", optional = true }
owo-colors = { version = ">=3.5, <5.0", default-features = false, optional = true }
supports-color = { version = ">=2.0.0, <4.0", optional = true }

Expand Down
11 changes: 10 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
# Change Log

## bpaf [0.9.10] - unreleased


## bpaf [0.9.10], bpaf_derive [0.5.10] - 2024-03-19
- due to dependency change colored output for legacy version is no longer supported
- Added `OptionParser::max_width` and `#[bpaf(max_width(xxx))]` to specify maximum
width of help output
- Added a custom path attribute to allow using of reexported bpaf
thanks @bzm3r
- support anywhere in bpaf_derive
thanks @vallentin
- small documentation improvements
thanks @Boshen
- minor shell completion improvements
- avoid panic in case of hidden but required parser argument (#345)

## bpaf [0.9.9] - 2024-01-17
- fix formatting in ambiguity error message
Expand Down
2 changes: 1 addition & 1 deletion bpaf_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bpaf_derive"
version = "0.5.7"
version = "0.5.10"
edition = "2018"
categories = ["command-line-interface"]
description = "Derive macros for bpaf Command Line Argument Parser"
Expand Down
2 changes: 1 addition & 1 deletion src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::{
/// assert!(value);
/// ```
pub struct Args<'a> {
items: Box<dyn Iterator<Item = OsString> + 'a>,
items: Box<dyn ExactSizeIterator<Item = OsString> + 'a>,
name: Option<String>,
#[cfg(feature = "autocomplete")]
c_rev: Option<usize>,
Expand Down
11 changes: 9 additions & 2 deletions src/complete_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,15 @@ impl State {
8 => render_bash(&items, &shell, full_lit),
9 => render_fish(&items, &shell, full_lit, self.path[0].as_str()),
unk => {
eprintln!("Unsupported output revision {}, you need to genenerate your shell completion files for the app", unk);
std::process::exit(1);
#[cfg(debug_assertions)]
{
eprintln!("Unsupported output revision {}, you need to genenerate your shell completion files for the app", unk);
std::process::exit(1);
}
#[cfg(not(debug_assertions))]
{
std::process::exit(0);
}
}
}.unwrap())
}
Expand Down
13 changes: 11 additions & 2 deletions src/complete_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ fn dump_bash_completer(name: &str) {
println!(
r#"_bpaf_dynamic_completion()
{{
source <( "$1" --bpaf-complete-rev=8 "${{COMP_WORDS[@]:1}}" )
line="$1 --bpaf-complete-rev=8 ${{COMP_WORDS[@]:1}}"
if [[ ${{COMP_WORDS[-1]}} == "" ]]; then
line="${{line}} \"\""
fi
source <( eval ${{line}})
}}
complete -o nosort -F _bpaf_dynamic_completion {name}"#,
name = name,
Expand All @@ -15,7 +19,12 @@ complete -o nosort -F _bpaf_dynamic_completion {name}"#,
fn dump_zsh_completer(name: &str) {
println!(
r#"#compdef {name}
source <( "${{words[1]}}" --bpaf-complete-rev=7 "${{words[@]:1}}" )
local line
line="${{words[1]}} --bpaf-complete-rev=7 ${{words[@]:1}}"
if [[ ${{words[-1]}} == "" ]]; then
line="${{line}} \"\""
fi
source <(eval ${{line}})
"#,
name = name
);
Expand Down
58 changes: 41 additions & 17 deletions src/complete_shell.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
use crate::{complete_gen::ShowComp, Error, Meta, Parser, State};

struct Shell<'a>(&'a str);

impl std::fmt::Display for Shell<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
f.write_char('\'')?;
for c in self.0.chars() {
if c == '\'' {
f.write_str("'\\''")
} else {
f.write_char(c)
}?
}
f.write_char('\'')?;
Ok(())
}
}

#[derive(Debug, Clone, Copy)]
/// Shell specific completion
#[non_exhaustive]
Expand Down Expand Up @@ -108,39 +126,45 @@ pub(crate) fn render_zsh(
for op in ops {
match op {
ShellComp::File { mask: None } => writeln!(res, "_files"),
ShellComp::File { mask: Some(mask) } => writeln!(res, "_files -g '{}'", mask),
ShellComp::File { mask: Some(mask) } => writeln!(res, "_files -g {}", Shell(mask)),
ShellComp::Dir { mask: None } => writeln!(res, "_files -/"),
ShellComp::Dir { mask: Some(mask) } => writeln!(res, "_files -/ -g '{}'", mask),
ShellComp::Raw { zsh, .. } => writeln!(res, "{}", zsh),
ShellComp::Dir { mask: Some(mask) } => writeln!(res, "_files -/ -g {}", Shell(mask)),
ShellComp::Raw { zsh, .. } => writeln!(res, "{}", Shell(zsh)),
ShellComp::Nothing => Ok(()),
}?;
}

if items.len() == 1 {
if items[0].subst.is_empty() {
writeln!(res, "compadd -- {:?}", items[0].pretty)?;
writeln!(res, "compadd -- {}", Shell(items[0].pretty.as_str()))?;
writeln!(res, "compadd ''")?;
return Ok(res);
} else {
return Ok(format!("compadd -- {:?}\n", items[0].subst));
return Ok(format!("compadd -- {}\n", Shell(items[0].subst.as_str())));
}
}
writeln!(res, "local -a descr")?;

for item in items {
writeln!(res, "descr=(\"{}\")", item)?;
writeln!(res, "descr=({})", Shell(&item.to_string()))?;
// writeln!(res, "args=(\"{}\")", item.subst)?;
if let Some(group) = &item.extra.group {
writeln!(
res,
"compadd -l -d descr -V {:?} -X {:?} -- {:?}",
group, group, item.subst,
"compadd -l -d descr -V {} -X {} -- {}",
Shell(group),
Shell(group),
Shell(&item.subst),
)?;
} else {
// it seems sorting as well as not sorting is done in a group,
// by default group contains just one element so and `-o nosort`
// does nothing, while `-V whatever` stops sorting...
writeln!(res, "compadd -l -V nosort -d descr -- {:?}", item.subst)?;
writeln!(
res,
"compadd -l -V nosort -d descr -- {}",
Shell(&item.subst)
)?;
}
}
Ok(res)
Expand All @@ -163,29 +187,29 @@ pub(crate) fn render_bash(
let mut res = String::new();

if items.is_empty() && ops.is_empty() {
return Ok(format!("COMPREPLY+=( {:?})\n", full_lit));
return Ok(format!("COMPREPLY+=({})\n", Shell(full_lit)));
}

for op in ops {
match op {
ShellComp::File { mask: None } => write!(res, "_filedir"),
ShellComp::File { mask: Some(mask) } => {
writeln!(res, "_filedir '{}'", bashmask(mask))
writeln!(res, "_filedir '{}'", Shell(bashmask(mask)))
}
ShellComp::Dir { mask: None } => write!(res, "_filedir -d"),
ShellComp::Dir { mask: Some(mask) } => {
writeln!(res, "_filedir -d '{}'", bashmask(mask))
writeln!(res, "_filedir -d '{}'", Shell(bashmask(mask)))
}
ShellComp::Raw { bash, .. } => writeln!(res, "{}", bash),
ShellComp::Raw { bash, .. } => writeln!(res, "{}", Shell(bash)),
ShellComp::Nothing => Ok(()),
}?;
}

if items.len() == 1 {
if items[0].subst.is_empty() {
writeln!(res, "COMPREPLY+=( {:?} '')", items[0].pretty)?;
writeln!(res, "COMPREPLY+=( {} '')", Shell(&items[0].pretty))?;
} else {
writeln!(res, "COMPREPLY+=( {:?} )\n", items[0].subst)?;
writeln!(res, "COMPREPLY+=( {} )\n", Shell(&items[0].subst))?;
}

return Ok(res);
Expand All @@ -195,10 +219,10 @@ pub(crate) fn render_bash(
if let Some(group) = &item.extra.group {
if prev != group {
prev = group;
writeln!(res, "COMPREPLY+=({:?})", group)?;
writeln!(res, "COMPREPLY+=({})", Shell(group))?;
}
}
writeln!(res, "COMPREPLY+=(\"{}\")", item)?;
writeln!(res, "COMPREPLY+=({})", Shell(&item.to_string()))?;
}

Ok(res)
Expand Down
2 changes: 1 addition & 1 deletion src/docs2/choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Options { desert: Some("apple") }
</div>


Only one value is consumed at once
Since parser consumes only one value you can't specify multiple flags of the same type


<div class='bpaf-doc'>
Expand Down
8 changes: 6 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,10 +597,14 @@ impl Message {
/// go over all the missing items, pick the left most scope
pub(crate) fn summarize_missing(items: &[MissingItem], inner: &Meta, args: &State) -> Message {
// missing items can belong to different scopes, pick the best scope to work with
let best_item = items
let best_item = match items
.iter()
.max_by_key(|item| (item.position, item.scope.start))
.unwrap();
{
Some(x) => x,
None => return Message::ParseSome("parser requires an extra flag, argument or parameter, but its name is hidden by the author"),
};

let mut best_scope = best_item.scope.clone();

let mut saw_command = false;
Expand Down
20 changes: 20 additions & 0 deletions tests/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,23 @@ fn strictly_positional_help() {
.unwrap_stderr();
assert_eq!(r, "`--help` is not expected in this context");
}

#[test]
fn hidden_required_field_is_valid_but_strange() {
let parser = short('a').req_flag(()).hide().to_options();

let r = parser.run_inner(&[]).unwrap_err().unwrap_stderr();

assert_eq!(r, "parser requires an extra flag, argument or parameter, but its name is hidden by the author");
}

#[test]
fn guard_on_fallback() {
let parser = short('a')
.argument::<usize>("A")
.fallback(10)
.guard(|a| *a < 10, "too big")
.to_options();
let r = parser.run_inner(&[]).unwrap_err().unwrap_stderr();
assert_eq!(r, "check failed: too big");
}

0 comments on commit 9088f7f

Please sign in to comment.