mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 14:52:33 +00:00
add env key for help msg
This commit is contained in:
parent
f254807c73
commit
bad5d19edb
12 changed files with 221 additions and 152 deletions
195
src/app/help.rs
195
src/app/help.rs
|
@ -10,7 +10,7 @@ use app::{App, AppSettings};
|
||||||
use app::parser::Parser;
|
use app::parser::Parser;
|
||||||
use args::{AnyArg, ArgSettings, DispOrder};
|
use args::{AnyArg, ArgSettings, DispOrder};
|
||||||
use errors::{Error, Result as ClapResult};
|
use errors::{Error, Result as ClapResult};
|
||||||
use fmt::{Format, Colorizer, ColorizerOption};
|
use fmt::{Colorizer, ColorizerOption, Format};
|
||||||
use app::usage;
|
use app::usage;
|
||||||
use map::VecMap;
|
use map::VecMap;
|
||||||
|
|
||||||
|
@ -31,13 +31,18 @@ const TAB: &'static str = " ";
|
||||||
|
|
||||||
// These are just convenient traits to make the code easier to read.
|
// These are just convenient traits to make the code easier to read.
|
||||||
trait ArgWithDisplay<'b, 'c>: AnyArg<'b, 'c> + Display {}
|
trait ArgWithDisplay<'b, 'c>: AnyArg<'b, 'c> + Display {}
|
||||||
impl<'b, 'c, T> ArgWithDisplay<'b, 'c> for T where T: AnyArg<'b, 'c> + Display {}
|
impl<'b, 'c, T> ArgWithDisplay<'b, 'c> for T
|
||||||
|
where
|
||||||
|
T: AnyArg<'b, 'c> + Display,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
trait ArgWithOrder<'b, 'c>: ArgWithDisplay<'b, 'c> + DispOrder {
|
trait ArgWithOrder<'b, 'c>: ArgWithDisplay<'b, 'c> + DispOrder {
|
||||||
fn as_base(&self) -> &ArgWithDisplay<'b, 'c>;
|
fn as_base(&self) -> &ArgWithDisplay<'b, 'c>;
|
||||||
}
|
}
|
||||||
impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T
|
impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T
|
||||||
where T: ArgWithDisplay<'b, 'c> + DispOrder
|
where
|
||||||
|
T: ArgWithDisplay<'b, 'c> + DispOrder,
|
||||||
{
|
{
|
||||||
fn as_base(&self) -> &ArgWithDisplay<'b, 'c> { self }
|
fn as_base(&self) -> &ArgWithDisplay<'b, 'c> { self }
|
||||||
}
|
}
|
||||||
|
@ -84,29 +89,34 @@ pub struct Help<'a> {
|
||||||
impl<'a> Help<'a> {
|
impl<'a> Help<'a> {
|
||||||
/// Create a new `Help` instance.
|
/// Create a new `Help` instance.
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
|
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
|
||||||
pub fn new(w: &'a mut Write,
|
pub fn new(
|
||||||
|
w: &'a mut Write,
|
||||||
next_line_help: bool,
|
next_line_help: bool,
|
||||||
hide_pv: bool,
|
hide_pv: bool,
|
||||||
color: bool,
|
color: bool,
|
||||||
cizer: Colorizer,
|
cizer: Colorizer,
|
||||||
term_w: Option<usize>,
|
term_w: Option<usize>,
|
||||||
max_w: Option<usize>,
|
max_w: Option<usize>,
|
||||||
use_long: bool)
|
use_long: bool,
|
||||||
-> Self {
|
) -> Self {
|
||||||
debugln!("Help::new;");
|
debugln!("Help::new;");
|
||||||
Help {
|
Help {
|
||||||
writer: w,
|
writer: w,
|
||||||
next_line_help: next_line_help,
|
next_line_help: next_line_help,
|
||||||
hide_pv: hide_pv,
|
hide_pv: hide_pv,
|
||||||
term_w: match term_w {
|
term_w: match term_w {
|
||||||
Some(width) => if width == 0 { usize::MAX } else { width },
|
Some(width) => if width == 0 {
|
||||||
None => {
|
usize::MAX
|
||||||
cmp::min(term_size::dimensions().map_or(120, |(w, _)| w),
|
} else {
|
||||||
|
width
|
||||||
|
},
|
||||||
|
None => cmp::min(
|
||||||
|
term_size::dimensions().map_or(120, |(w, _)| w),
|
||||||
match max_w {
|
match max_w {
|
||||||
None | Some(0) => usize::MAX,
|
None | Some(0) => usize::MAX,
|
||||||
Some(mw) => mw,
|
Some(mw) => mw,
|
||||||
})
|
},
|
||||||
}
|
),
|
||||||
},
|
},
|
||||||
color: color,
|
color: color,
|
||||||
cizer: cizer,
|
cizer: cizer,
|
||||||
|
@ -139,7 +149,12 @@ impl<'a> Help<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn _write_parser_help(w: &'a mut Write, parser: &Parser, stderr: bool, use_long: bool) -> ClapResult<()> {
|
pub fn _write_parser_help(
|
||||||
|
w: &'a mut Write,
|
||||||
|
parser: &Parser,
|
||||||
|
stderr: bool,
|
||||||
|
use_long: bool,
|
||||||
|
) -> ClapResult<()> {
|
||||||
debugln!("Help::write_parser_help;");
|
debugln!("Help::write_parser_help;");
|
||||||
let nlh = parser.is_set(AppSettings::NextLineHelp);
|
let nlh = parser.is_set(AppSettings::NextLineHelp);
|
||||||
let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp);
|
let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp);
|
||||||
|
@ -148,15 +163,16 @@ impl<'a> Help<'a> {
|
||||||
use_stderr: stderr,
|
use_stderr: stderr,
|
||||||
when: parser.color(),
|
when: parser.color(),
|
||||||
});
|
});
|
||||||
Self::new(w,
|
Self::new(
|
||||||
|
w,
|
||||||
nlh,
|
nlh,
|
||||||
hide_v,
|
hide_v,
|
||||||
color,
|
color,
|
||||||
cizer,
|
cizer,
|
||||||
parser.meta.term_w,
|
parser.meta.term_w,
|
||||||
parser.meta.max_w,
|
parser.meta.max_w,
|
||||||
use_long)
|
use_long,
|
||||||
.write_help(parser)
|
).write_help(parser)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the parser help to the wrapped stream.
|
/// Writes the parser help to the wrapped stream.
|
||||||
|
@ -177,15 +193,15 @@ impl<'a> Help<'a> {
|
||||||
impl<'a> Help<'a> {
|
impl<'a> Help<'a> {
|
||||||
/// Writes help for each argument in the order they were declared to the wrapped stream.
|
/// Writes help for each argument in the order they were declared to the wrapped stream.
|
||||||
fn write_args_unsorted<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
|
fn write_args_unsorted<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
|
||||||
where I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>
|
where
|
||||||
|
I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>,
|
||||||
{
|
{
|
||||||
debugln!("Help::write_args_unsorted;");
|
debugln!("Help::write_args_unsorted;");
|
||||||
// The shortest an arg can legally be is 2 (i.e. '-x')
|
// The shortest an arg can legally be is 2 (i.e. '-x')
|
||||||
self.longest = 2;
|
self.longest = 2;
|
||||||
let mut arg_v = Vec::with_capacity(10);
|
let mut arg_v = Vec::with_capacity(10);
|
||||||
for arg in args.filter(|arg| {
|
for arg in args.filter(|arg| {
|
||||||
!(arg.is_set(ArgSettings::Hidden)) ||
|
!(arg.is_set(ArgSettings::Hidden)) || arg.is_set(ArgSettings::NextLineHelp)
|
||||||
arg.is_set(ArgSettings::NextLineHelp)
|
|
||||||
}) {
|
}) {
|
||||||
if arg.longest_filter() {
|
if arg.longest_filter() {
|
||||||
self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str()));
|
self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str()));
|
||||||
|
@ -206,7 +222,8 @@ impl<'a> Help<'a> {
|
||||||
|
|
||||||
/// Sorts arguments by length and display order and write their help to the wrapped stream.
|
/// Sorts arguments by length and display order and write their help to the wrapped stream.
|
||||||
fn write_args<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
|
fn write_args<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
|
||||||
where I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>
|
where
|
||||||
|
I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>,
|
||||||
{
|
{
|
||||||
debugln!("Help::write_args;");
|
debugln!("Help::write_args;");
|
||||||
// The shortest an arg can legally be is 2 (i.e. '-x')
|
// The shortest an arg can legally be is 2 (i.e. '-x')
|
||||||
|
@ -345,10 +362,12 @@ impl<'a> Help<'a> {
|
||||||
debugln!("Help::val: force_next_line...{:?}", self.force_next_line);
|
debugln!("Help::val: force_next_line...{:?}", self.force_next_line);
|
||||||
debugln!("Help::val: nlh...{:?}", nlh);
|
debugln!("Help::val: nlh...{:?}", nlh);
|
||||||
debugln!("Help::val: taken...{}", taken);
|
debugln!("Help::val: taken...{}", taken);
|
||||||
debugln!("Help::val: help_width > (width - taken)...{} > ({} - {})",
|
debugln!(
|
||||||
|
"Help::val: help_width > (width - taken)...{} > ({} - {})",
|
||||||
h_w,
|
h_w,
|
||||||
self.term_w,
|
self.term_w,
|
||||||
taken);
|
taken
|
||||||
|
);
|
||||||
debugln!("Help::val: longest...{}", self.longest);
|
debugln!("Help::val: longest...{}", self.longest);
|
||||||
debug!("Help::val: next_line...");
|
debug!("Help::val: next_line...");
|
||||||
if !(nlh || self.force_next_line) {
|
if !(nlh || self.force_next_line) {
|
||||||
|
@ -372,7 +391,10 @@ impl<'a> Help<'a> {
|
||||||
}
|
}
|
||||||
} else if !(nlh || self.force_next_line) {
|
} else if !(nlh || self.force_next_line) {
|
||||||
sdebugln!("No, and not next_line");
|
sdebugln!("No, and not next_line");
|
||||||
write_nspaces!(self.writer, self.longest + 4 - (str_width(arg.to_string().as_str())));
|
write_nspaces!(
|
||||||
|
self.writer,
|
||||||
|
self.longest + 4 - (str_width(arg.to_string().as_str()))
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
sdebugln!("No");
|
sdebugln!("No");
|
||||||
}
|
}
|
||||||
|
@ -383,19 +405,25 @@ impl<'a> Help<'a> {
|
||||||
debugln!("Help::write_before_after_help;");
|
debugln!("Help::write_before_after_help;");
|
||||||
let mut help = String::from(h);
|
let mut help = String::from(h);
|
||||||
// determine if our help fits or needs to wrap
|
// determine if our help fits or needs to wrap
|
||||||
debugln!("Help::write_before_after_help: Term width...{}",
|
debugln!(
|
||||||
self.term_w);
|
"Help::write_before_after_help: Term width...{}",
|
||||||
|
self.term_w
|
||||||
|
);
|
||||||
let too_long = str_width(h) >= self.term_w;
|
let too_long = str_width(h) >= self.term_w;
|
||||||
|
|
||||||
debug!("Help::write_before_after_help: Too long...");
|
debug!("Help::write_before_after_help: Too long...");
|
||||||
if too_long || h.contains("{n}") {
|
if too_long || h.contains("{n}") {
|
||||||
sdebugln!("Yes");
|
sdebugln!("Yes");
|
||||||
debugln!("Help::write_before_after_help: help: {}", help);
|
debugln!("Help::write_before_after_help: help: {}", help);
|
||||||
debugln!("Help::write_before_after_help: help width: {}",
|
debugln!(
|
||||||
str_width(&*help));
|
"Help::write_before_after_help: help width: {}",
|
||||||
|
str_width(&*help)
|
||||||
|
);
|
||||||
// Determine how many newlines we need to insert
|
// Determine how many newlines we need to insert
|
||||||
debugln!("Help::write_before_after_help: Usable space: {}",
|
debugln!(
|
||||||
self.term_w);
|
"Help::write_before_after_help: Usable space: {}",
|
||||||
|
self.term_w
|
||||||
|
);
|
||||||
help = wrap_help(&help.replace("{n}", "\n"), self.term_w);
|
help = wrap_help(&help.replace("{n}", "\n"), self.term_w);
|
||||||
} else {
|
} else {
|
||||||
sdebugln!("No");
|
sdebugln!("No");
|
||||||
|
@ -467,18 +495,32 @@ impl<'a> Help<'a> {
|
||||||
if !a.is_set(ArgSettings::HideDefaultValue) {
|
if !a.is_set(ArgSettings::HideDefaultValue) {
|
||||||
if let Some(pv) = a.default_val() {
|
if let Some(pv) = a.default_val() {
|
||||||
debugln!("Help::spec_vals: Found default value...[{:?}]", pv);
|
debugln!("Help::spec_vals: Found default value...[{:?}]", pv);
|
||||||
spec_vals.push(format!(" [default: {}]",
|
spec_vals.push(format!(
|
||||||
|
" [default: {}]",
|
||||||
if self.color {
|
if self.color {
|
||||||
self.cizer.good(pv.to_string_lossy())
|
self.cizer.good(pv.to_string_lossy())
|
||||||
} else {
|
} else {
|
||||||
Format::None(pv.to_string_lossy())
|
Format::None(pv.to_string_lossy())
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
// FIXME: add [env::{}: {}] here
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ref env) = a.env() {
|
||||||
|
debugln!(
|
||||||
|
"Help::spec_vals: Found environment variable...[{:?}:{:?}]",
|
||||||
|
env.0,
|
||||||
|
env.1
|
||||||
|
);
|
||||||
|
spec_vals.push(format!(
|
||||||
|
" [env:{}:{}]",
|
||||||
|
env.0.to_string_lossy(),
|
||||||
|
env.1.to_string_lossy()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if let Some(ref aliases) = a.aliases() {
|
if let Some(ref aliases) = a.aliases() {
|
||||||
debugln!("Help::spec_vals: Found aliases...{:?}", aliases);
|
debugln!("Help::spec_vals: Found aliases...{:?}", aliases);
|
||||||
spec_vals.push(format!(" [aliases: {}]",
|
spec_vals.push(format!(
|
||||||
|
" [aliases: {}]",
|
||||||
if self.color {
|
if self.color {
|
||||||
aliases
|
aliases
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -487,17 +529,20 @@ impl<'a> Help<'a> {
|
||||||
.join(", ")
|
.join(", ")
|
||||||
} else {
|
} else {
|
||||||
aliases.join(", ")
|
aliases.join(", ")
|
||||||
}));
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if !self.hide_pv && !a.is_set(ArgSettings::HidePossibleValues) {
|
if !self.hide_pv && !a.is_set(ArgSettings::HidePossibleValues) {
|
||||||
if let Some(pv) = a.possible_vals() {
|
if let Some(pv) = a.possible_vals() {
|
||||||
debugln!("Help::spec_vals: Found possible vals...{:?}", pv);
|
debugln!("Help::spec_vals: Found possible vals...{:?}", pv);
|
||||||
spec_vals.push(if self.color {
|
spec_vals.push(if self.color {
|
||||||
format!(" [values: {}]",
|
format!(
|
||||||
|
" [values: {}]",
|
||||||
pv.iter()
|
pv.iter()
|
||||||
.map(|v| format!("{}", self.cizer.good(v)))
|
.map(|v| format!("{}", self.cizer.good(v)))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", "))
|
.join(", ")
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(" [values: {}]", pv.join(", "))
|
format!(" [values: {}]", pv.join(", "))
|
||||||
});
|
});
|
||||||
|
@ -581,10 +626,9 @@ impl<'a> Help<'a> {
|
||||||
for sc in parser
|
for sc in parser
|
||||||
.subcommands
|
.subcommands
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| !s.p.is_set(AppSettings::Hidden)) {
|
.filter(|s| !s.p.is_set(AppSettings::Hidden))
|
||||||
let btm = ord_m
|
{
|
||||||
.entry(sc.p.meta.disp_ord)
|
let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new());
|
||||||
.or_insert(BTreeMap::new());
|
|
||||||
self.longest = cmp::max(self.longest, str_width(sc.p.meta.name.as_str()));
|
self.longest = cmp::max(self.longest, str_width(sc.p.meta.name.as_str()));
|
||||||
//self.longest = cmp::max(self.longest, sc.p.meta.name.len());
|
//self.longest = cmp::max(self.longest, sc.p.meta.name.len());
|
||||||
btm.insert(sc.p.meta.name.clone(), sc.clone());
|
btm.insert(sc.p.meta.name.clone(), sc.clone());
|
||||||
|
@ -677,10 +721,12 @@ impl<'a> Help<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
color!(self, "\nUSAGE:", warning)?;
|
color!(self, "\nUSAGE:", warning)?;
|
||||||
write!(self.writer,
|
write!(
|
||||||
|
self.writer,
|
||||||
"\n{}{}\n\n",
|
"\n{}{}\n\n",
|
||||||
TAB,
|
TAB,
|
||||||
usage::create_usage_no_title(parser, &[]))?;
|
usage::create_usage_no_title(parser, &[])
|
||||||
|
)?;
|
||||||
|
|
||||||
let flags = parser.has_flags();
|
let flags = parser.has_flags();
|
||||||
let pos = parser.has_positionals();
|
let pos = parser.has_positionals();
|
||||||
|
@ -746,21 +792,20 @@ fn copy_until<R: Read, W: Write>(r: &mut R, w: &mut W, delimiter_byte: u8) -> Co
|
||||||
/// - `None`: The reader was consumed.
|
/// - `None`: The reader was consumed.
|
||||||
/// - `Some(Ok(0))`: No tag was captured but the reader still contains data.
|
/// - `Some(Ok(0))`: No tag was captured but the reader still contains data.
|
||||||
/// - `Some(Ok(length>0))`: a tag with `length` was captured to the `tag_buffer`.
|
/// - `Some(Ok(length>0))`: a tag with `length` was captured to the `tag_buffer`.
|
||||||
fn copy_and_capture<R: Read, W: Write>(r: &mut R,
|
fn copy_and_capture<R: Read, W: Write>(
|
||||||
|
r: &mut R,
|
||||||
w: &mut W,
|
w: &mut W,
|
||||||
tag_buffer: &mut Cursor<Vec<u8>>)
|
tag_buffer: &mut Cursor<Vec<u8>>,
|
||||||
-> Option<io::Result<usize>> {
|
) -> Option<io::Result<usize>> {
|
||||||
use self::CopyUntilResult::*;
|
use self::CopyUntilResult::*;
|
||||||
debugln!("copy_and_capture;");
|
debugln!("copy_and_capture;");
|
||||||
|
|
||||||
// Find the opening byte.
|
// Find the opening byte.
|
||||||
match copy_until(r, w, b'{') {
|
match copy_until(r, w, b'{') {
|
||||||
|
|
||||||
// The end of the reader was reached without finding the opening tag.
|
// The end of the reader was reached without finding the opening tag.
|
||||||
// (either with or without having copied data to the writer)
|
// (either with or without having copied data to the writer)
|
||||||
// Return None indicating that we are done.
|
// Return None indicating that we are done.
|
||||||
ReaderEmpty |
|
ReaderEmpty | DelimiterNotFound(_) => None,
|
||||||
DelimiterNotFound(_) => None,
|
|
||||||
|
|
||||||
// Something went wrong.
|
// Something went wrong.
|
||||||
ReadError(e) | WriteError(e) => Some(Err(e)),
|
ReadError(e) | WriteError(e) => Some(Err(e)),
|
||||||
|
@ -768,7 +813,6 @@ fn copy_and_capture<R: Read, W: Write>(r: &mut R,
|
||||||
// The opening byte was found.
|
// The opening byte was found.
|
||||||
// (either with or without having copied data to the writer)
|
// (either with or without having copied data to the writer)
|
||||||
DelimiterFound(_) => {
|
DelimiterFound(_) => {
|
||||||
|
|
||||||
// Lets reset the buffer first and find out how long it is.
|
// Lets reset the buffer first and find out how long it is.
|
||||||
tag_buffer.set_position(0);
|
tag_buffer.set_position(0);
|
||||||
let buffer_size = tag_buffer.get_ref().len();
|
let buffer_size = tag_buffer.get_ref().len();
|
||||||
|
@ -776,7 +820,6 @@ fn copy_and_capture<R: Read, W: Write>(r: &mut R,
|
||||||
// Find the closing byte,limiting the reader to the length of the buffer.
|
// Find the closing byte,limiting the reader to the length of the buffer.
|
||||||
let mut rb = r.take(buffer_size as u64);
|
let mut rb = r.take(buffer_size as u64);
|
||||||
match copy_until(&mut rb, tag_buffer, b'}') {
|
match copy_until(&mut rb, tag_buffer, b'}') {
|
||||||
|
|
||||||
// We were already at the end of the reader.
|
// We were already at the end of the reader.
|
||||||
// Return None indicating that we are done.
|
// Return None indicating that we are done.
|
||||||
ReaderEmpty => None,
|
ReaderEmpty => None,
|
||||||
|
@ -788,17 +831,13 @@ fn copy_and_capture<R: Read, W: Write>(r: &mut R,
|
||||||
// The end of the reader was found without finding the closing tag.
|
// The end of the reader was found without finding the closing tag.
|
||||||
// Write the opening byte and captured text to the writer.
|
// Write the opening byte and captured text to the writer.
|
||||||
// Return 0 indicating that nothing was caputred but the reader still contains data.
|
// Return 0 indicating that nothing was caputred but the reader still contains data.
|
||||||
DelimiterNotFound(not_tag_length) => {
|
DelimiterNotFound(not_tag_length) => match w.write(b"{") {
|
||||||
match w.write(b"{") {
|
|
||||||
Err(e) => Some(Err(e)),
|
Err(e) => Some(Err(e)),
|
||||||
_ => {
|
_ => match w.write(&tag_buffer.get_ref()[0..not_tag_length]) {
|
||||||
match w.write(&tag_buffer.get_ref()[0..not_tag_length]) {
|
|
||||||
Err(e) => Some(Err(e)),
|
Err(e) => Some(Err(e)),
|
||||||
_ => Some(Ok(0)),
|
_ => Some(Ok(0)),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadError(e) | WriteError(e) => Some(Err(e)),
|
ReadError(e) | WriteError(e) => Some(Err(e)),
|
||||||
}
|
}
|
||||||
|
@ -849,10 +888,12 @@ impl<'a> Help<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
debugln!("Help::write_template_help:iter: tag_buf={};", unsafe {
|
debugln!("Help::write_template_help:iter: tag_buf={};", unsafe {
|
||||||
String::from_utf8_unchecked(tag_buf.get_ref()[0..tag_length]
|
String::from_utf8_unchecked(
|
||||||
|
tag_buf.get_ref()[0..tag_length]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&i| i)
|
.map(|&i| i)
|
||||||
.collect::<Vec<_>>())
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
match &tag_buf.get_ref()[0..tag_length] {
|
match &tag_buf.get_ref()[0..tag_length] {
|
||||||
b"?" => {
|
b"?" => {
|
||||||
|
@ -862,24 +903,32 @@ impl<'a> Help<'a> {
|
||||||
self.write_bin_name(parser)?;
|
self.write_bin_name(parser)?;
|
||||||
}
|
}
|
||||||
b"version" => {
|
b"version" => {
|
||||||
write!(self.writer,
|
write!(
|
||||||
|
self.writer,
|
||||||
"{}",
|
"{}",
|
||||||
parser.meta.version.unwrap_or("unknown version"))?;
|
parser.meta.version.unwrap_or("unknown version")
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
b"author" => {
|
b"author" => {
|
||||||
write!(self.writer,
|
write!(
|
||||||
|
self.writer,
|
||||||
"{}",
|
"{}",
|
||||||
parser.meta.author.unwrap_or("unknown author"))?;
|
parser.meta.author.unwrap_or("unknown author")
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
b"about" => {
|
b"about" => {
|
||||||
write!(self.writer,
|
write!(
|
||||||
|
self.writer,
|
||||||
"{}",
|
"{}",
|
||||||
parser.meta.about.unwrap_or("unknown about"))?;
|
parser.meta.about.unwrap_or("unknown about")
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
b"long-about" => {
|
b"long-about" => {
|
||||||
write!(self.writer,
|
write!(
|
||||||
|
self.writer,
|
||||||
"{}",
|
"{}",
|
||||||
parser.meta.long_about.unwrap_or("unknown about"))?;
|
parser.meta.long_about.unwrap_or("unknown about")
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
b"usage" => {
|
b"usage" => {
|
||||||
write!(self.writer, "{}", usage::create_usage_no_title(parser, &[]))?;
|
write!(self.writer, "{}", usage::create_usage_no_title(parser, &[]))?;
|
||||||
|
@ -907,14 +956,18 @@ impl<'a> Help<'a> {
|
||||||
self.write_subcommands(parser)?;
|
self.write_subcommands(parser)?;
|
||||||
}
|
}
|
||||||
b"after-help" => {
|
b"after-help" => {
|
||||||
write!(self.writer,
|
write!(
|
||||||
|
self.writer,
|
||||||
"{}",
|
"{}",
|
||||||
parser.meta.more_help.unwrap_or("unknown after-help"))?;
|
parser.meta.more_help.unwrap_or("unknown after-help")
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
b"before-help" => {
|
b"before-help" => {
|
||||||
write!(self.writer,
|
write!(
|
||||||
|
self.writer,
|
||||||
"{}",
|
"{}",
|
||||||
parser.meta.pre_help.unwrap_or("unknown before-help"))?;
|
parser.meta.pre_help.unwrap_or("unknown before-help")
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
// Unknown tag, write it back.
|
// Unknown tag, write it back.
|
||||||
r => {
|
r => {
|
||||||
|
|
|
@ -1796,7 +1796,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> {
|
||||||
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
|
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn from_env(&self) -> Option<&OsString> { None }
|
fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)> { None }
|
||||||
fn longest_filter(&self) -> bool { true }
|
fn longest_filter(&self) -> bool { true }
|
||||||
fn aliases(&self) -> Option<Vec<&'e str>> {
|
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||||
if let Some(ref aliases) = self.p.meta.aliases {
|
if let Some(ref aliases) = self.p.meta.aliases {
|
||||||
|
|
|
@ -1789,19 +1789,19 @@ impl<'a, 'b> Parser<'a, 'b>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_from_env(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
|
pub fn add_env(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
|
||||||
macro_rules! add_val {
|
macro_rules! add_val {
|
||||||
($_self:ident, $a:ident, $m:ident) => {
|
($_self:ident, $a:ident, $m:ident) => {
|
||||||
if let Some(ref val) = $a.v.from_env {
|
if let Some(ref val) = $a.v.env {
|
||||||
if $m.get($a.b.name).map(|ma| ma.vals.len()).map(|len| len == 0).unwrap_or(false) {
|
if $m.get($a.b.name).map(|ma| ma.vals.len()).map(|len| len == 0).unwrap_or(false) {
|
||||||
$_self.add_val_to_arg($a, OsStr::new(val), $m)?;
|
$_self.add_val_to_arg($a, OsStr::new(&val.1), $m)?;
|
||||||
|
|
||||||
if $_self.cache.map_or(true, |name| name != $a.name()) {
|
if $_self.cache.map_or(true, |name| name != $a.name()) {
|
||||||
arg_post_processing!($_self, $a, $m);
|
arg_post_processing!($_self, $a, $m);
|
||||||
$_self.cache = Some($a.name());
|
$_self.cache = Some($a.name());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$_self.add_val_to_arg($a, OsStr::new(val), $m)?;
|
$_self.add_val_to_arg($a, OsStr::new(&val.1), $m)?;
|
||||||
|
|
||||||
if $_self.cache.map_or(true, |name| name != $a.name()) {
|
if $_self.cache.map_or(true, |name| name != $a.name()) {
|
||||||
arg_post_processing!($_self, $a, $m);
|
arg_post_processing!($_self, $a, $m);
|
||||||
|
|
|
@ -28,7 +28,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
|
||||||
-> ClapResult<()> {
|
-> ClapResult<()> {
|
||||||
debugln!("Validator::validate;");
|
debugln!("Validator::validate;");
|
||||||
let mut reqs_validated = false;
|
let mut reqs_validated = false;
|
||||||
self.0.add_from_env(matcher)?;
|
self.0.add_env(matcher)?;
|
||||||
self.0.add_defaults(matcher)?;
|
self.0.add_defaults(matcher)?;
|
||||||
if let ParseResult::Opt(a) = needs_val_of {
|
if let ParseResult::Opt(a) = needs_val_of {
|
||||||
debugln!("Validator::validate: needs_val_of={:?}", a);
|
debugln!("Validator::validate: needs_val_of={:?}", a);
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub trait AnyArg<'n, 'e>: std_fmt::Display {
|
||||||
fn long_help(&self) -> Option<&'e str>;
|
fn long_help(&self) -> Option<&'e str>;
|
||||||
fn default_val(&self) -> Option<&'e OsStr>;
|
fn default_val(&self) -> Option<&'e OsStr>;
|
||||||
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>>;
|
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>>;
|
||||||
fn from_env(&self) -> Option<&OsString>;
|
fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)>;
|
||||||
fn longest_filter(&self) -> bool;
|
fn longest_filter(&self) -> bool;
|
||||||
fn val_terminator(&self) -> Option<&'e str>;
|
fn val_terminator(&self) -> Option<&'e str>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ impl<'a, 'b> Arg<'a, 'b> {
|
||||||
"default_value" => yaml_to_str!(a, v, default_value),
|
"default_value" => yaml_to_str!(a, v, default_value),
|
||||||
"default_value_if" => yaml_tuple3!(a, v, default_value_if),
|
"default_value_if" => yaml_tuple3!(a, v, default_value_if),
|
||||||
"default_value_ifs" => yaml_tuple3!(a, v, default_value_if),
|
"default_value_ifs" => yaml_tuple3!(a, v, default_value_if),
|
||||||
"from_env" => yaml_to_str!(a, v, from_env),
|
"env" => yaml_to_str!(a, v, env),
|
||||||
"value_names" => yaml_vec_or_str!(v, a, value_name),
|
"value_names" => yaml_vec_or_str!(v, a, value_name),
|
||||||
"groups" => yaml_vec_or_str!(v, a, group),
|
"groups" => yaml_vec_or_str!(v, a, group),
|
||||||
"requires" => yaml_vec_or_str!(v, a, requires),
|
"requires" => yaml_vec_or_str!(v, a, requires),
|
||||||
|
@ -3315,10 +3315,10 @@ impl<'a, 'b> Arg<'a, 'b> {
|
||||||
/// Specifies that if the value is not passed in as an argument, that it should be retrieved
|
/// Specifies that if the value is not passed in as an argument, that it should be retrieved
|
||||||
/// from the environment if available. If it is not present in the environment, then default
|
/// from the environment if available. If it is not present in the environment, then default
|
||||||
/// rules will apply.
|
/// rules will apply.
|
||||||
pub fn from_env<K: AsRef<OsStr>>(mut self, name: K) -> Self {
|
pub fn env(mut self, name: &'a OsStr) -> Self {
|
||||||
self.setb(ArgSettings::TakesValue);
|
self.setb(ArgSettings::TakesValue);
|
||||||
|
|
||||||
self.v.from_env = env::var_os(name);
|
self.v.env = env::var_os(name).map(|value| (name, value));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
|
||||||
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
|
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn from_env(&self) -> Option<&OsString> { None }
|
fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)> { None }
|
||||||
fn longest_filter(&self) -> bool { self.s.long.is_some() }
|
fn longest_filter(&self) -> bool { self.s.long.is_some() }
|
||||||
fn aliases(&self) -> Option<Vec<&'e str>> {
|
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||||
if let Some(ref aliases) = self.s.aliases {
|
if let Some(ref aliases) = self.s.aliases {
|
||||||
|
|
|
@ -140,7 +140,9 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
|
||||||
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
|
fn default_vals_ifs(&self) -> Option<map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
|
||||||
self.v.default_vals_ifs.as_ref().map(|vm| vm.values())
|
self.v.default_vals_ifs.as_ref().map(|vm| vm.values())
|
||||||
}
|
}
|
||||||
fn from_env(&self) -> Option<&OsString> { self.v.from_env.as_ref() }
|
fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)> {
|
||||||
|
self.v.env.as_ref().map(|&(key, ref value)| (key, value))
|
||||||
|
}
|
||||||
fn longest_filter(&self) -> bool { true }
|
fn longest_filter(&self) -> bool { true }
|
||||||
fn aliases(&self) -> Option<Vec<&'e str>> {
|
fn aliases(&self) -> Option<Vec<&'e str>> {
|
||||||
if let Some(ref aliases) = self.s.aliases {
|
if let Some(ref aliases) = self.s.aliases {
|
||||||
|
|
|
@ -152,7 +152,12 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
|
||||||
self.v.default_vals_ifs.as_ref().map(|vm| vm.values())
|
self.v.default_vals_ifs.as_ref().map(|vm| vm.values())
|
||||||
}
|
}
|
||||||
fn default_val(&self) -> Option<&'e OsStr> { self.v.default_val }
|
fn default_val(&self) -> Option<&'e OsStr> { self.v.default_val }
|
||||||
fn from_env(&self) -> Option<&OsString> { self.v.from_env.as_ref() }
|
fn env<'s>(&'s self) -> Option<(&'n OsStr, &'s OsString)> {
|
||||||
|
self.v
|
||||||
|
.env
|
||||||
|
.as_ref()
|
||||||
|
.map(|&(key, ref value)| (key, value))
|
||||||
|
}
|
||||||
fn longest_filter(&self) -> bool { true }
|
fn longest_filter(&self) -> bool { true }
|
||||||
fn aliases(&self) -> Option<Vec<&'e str>> { None }
|
fn aliases(&self) -> Option<Vec<&'e str>> { None }
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ where
|
||||||
pub val_delim: Option<char>,
|
pub val_delim: Option<char>,
|
||||||
pub default_val: Option<&'b OsStr>,
|
pub default_val: Option<&'b OsStr>,
|
||||||
pub default_vals_ifs: Option<VecMap<(&'a str, Option<&'b OsStr>, &'b OsStr)>>,
|
pub default_vals_ifs: Option<VecMap<(&'a str, Option<&'b OsStr>, &'b OsStr)>>,
|
||||||
pub from_env: Option<OsString>,
|
pub env: Option<(&'a OsStr, OsString)>,
|
||||||
pub terminator: Option<&'b str>,
|
pub terminator: Option<&'b str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ impl<'n, 'e> Default for Valued<'n, 'e> {
|
||||||
val_delim: None,
|
val_delim: None,
|
||||||
default_val: None,
|
default_val: None,
|
||||||
default_vals_ifs: None,
|
default_vals_ifs: None,
|
||||||
from_env: None,
|
env: None,
|
||||||
terminator: None,
|
terminator: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -479,16 +479,3 @@ fn conditional_reqs_pass() {
|
||||||
assert_eq!(m.value_of("output"), Some("other"));
|
assert_eq!(m.value_of("output"), Some("other"));
|
||||||
assert_eq!(m.value_of("input"), Some("some"));
|
assert_eq!(m.value_of("input"), Some("some"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn from_env_no_default() {
|
|
||||||
std::env::set_var("CLP_TEST_ENV", "from_env");
|
|
||||||
|
|
||||||
let r = App::new("df")
|
|
||||||
.arg(Arg::from_usage("[arg] 'some opt'").from_env("CLP_TEST_ENV"))
|
|
||||||
.get_matches_from_safe(vec![""]);
|
|
||||||
assert!(r.is_ok());
|
|
||||||
let m = r.unwrap();
|
|
||||||
// assert!(m.is_present("arg")); // TODO: should this be true?
|
|
||||||
assert_eq!(m.value_of("arg").unwrap(), "from_env");
|
|
||||||
}
|
|
||||||
|
|
22
tests/env.rs
Normal file
22
tests/env.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
extern crate clap;
|
||||||
|
extern crate regex;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
use clap::{App, Arg, ErrorKind};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn env_no_default() {
|
||||||
|
env::set_var("CLP_TEST_ENV", "env");
|
||||||
|
|
||||||
|
let r = App::new("df")
|
||||||
|
.arg(
|
||||||
|
Arg::from_usage("[arg] 'some opt'").env(OsStr::new("CLP_TEST_ENV")),
|
||||||
|
)
|
||||||
|
.get_matches_from_safe(vec![""]);
|
||||||
|
assert!(r.is_ok());
|
||||||
|
let m = r.unwrap();
|
||||||
|
// assert!(m.is_present("arg")); // TODO: should this be true?
|
||||||
|
assert_eq!(m.value_of("arg").unwrap(), "env");
|
||||||
|
}
|
Loading…
Reference in a new issue