diff --git a/Cargo.toml b/Cargo.toml index 214cf622520..8300b52b894 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ color = ["ansi_term"] # for building with nightly and unstable features unstable=[] +# for building with debug messages +debug=[] + [dependencies.strsim] version = "0.4.0" optional = true diff --git a/clap-tests/run_tests.py b/clap-tests/run_tests.py index f0f29d8ffc1..f6bea648334 100755 --- a/clap-tests/run_tests.py +++ b/clap-tests/run_tests.py @@ -298,9 +298,15 @@ def pass_fail(name, check, good): def main(): for cmd, cmd_v in cmds.items(): - proc = subprocess.Popen(cmd_v[0], shell=True, stdout=subprocess.PIPE, universal_newlines=True) - out = _ansi.sub('', proc.communicate()[0].strip()) - pass_fail(cmd, out, cmd_v[1]) + proc = subprocess.Popen(cmd_v[0], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + out, err = proc.communicate() + out = _ansi.sub('', out.strip()) + err = _ansi.sub('', err.strip()) + if out: + pass_fail(cmd, out, cmd_v[1]) + else: + pass_fail(cmd, err, cmd_v[1]) + if failed: print('One or more tests failed') return 1 diff --git a/src/app.rs b/src/app.rs index 4c5229e9172..a55fb561382 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,15 +1,10 @@ -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::HashSet; -use std::collections::HashMap; -use std::collections::VecDeque; +use std::collections::{BTreeMap, BTreeSet, HashSet, HashMap, VecDeque}; use std::env; -use std::io::{self, BufRead}; +use std::io::{self, BufRead, Write}; use std::path::Path; -use std::process; -use args::{ ArgMatches, Arg, SubCommand, MatchedArg}; -use args::{ FlagBuilder, OptBuilder, PosBuilder}; +use args::{ArgMatches, Arg, SubCommand, MatchedArg}; +use args::{FlagBuilder, OptBuilder, PosBuilder}; use args::ArgGroup; use fmt::Format; @@ -22,8 +17,8 @@ use strsim; /// `Some("foo")`, whereas "blark" would yield `None`. #[cfg(feature = "suggestions")] fn did_you_mean<'a, T, I>(v: &str, possible_values: I) -> Option<&'a str> - where T: AsRef + 'a, - I: IntoIterator { + where T: AsRef + 'a, + I: IntoIterator { let mut candidate: Option<(f64, &str)> = None; for pv in possible_values.into_iter() { @@ -41,8 +36,8 @@ fn did_you_mean<'a, T, I>(v: &str, possible_values: I) -> Option<&'a str> #[cfg(not(feature = "suggestions"))] fn did_you_mean<'a, T, I>(_: &str, _: I) -> Option<&'a str> - where T: AsRef + 'a, - I: IntoIterator { + where T: AsRef + 'a, + I: IntoIterator { None } @@ -1202,13 +1197,13 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ usage } - // Prints the usage statement to the user - fn print_usage(&self, more_info: bool, matches: Option>) { - print!("{}",self.create_usage(matches)); - if more_info { - println!("\n\nFor more information try {}", Format::Good("--help")); - } - } + // // Prints the usage statement to the user + // fn print_usage(&self, more_info: bool, matches: Option>) { + // print!("{}",self.create_usage(matches)); + // if more_info { + // println!("\n\nFor more information try {}", Format::Good("--help")); + // } + // } // Prints the full help message to the user fn print_help(&self) { @@ -1216,7 +1211,12 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ println!("{}", h); return } - self.print_version(false); + + // Print the version + print!("{} {}\n", &self.bin_name.clone().unwrap_or( + self.name.clone())[..].replace(" ", "-"), + self.version.unwrap_or("") + ); let flags = !self.flags.is_empty(); let pos = !self.positionals_idx.is_empty(); let opts = !self.opts.is_empty(); @@ -1265,99 +1265,102 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } if let Some(author) = self.author { - println!("{}", author); + print!("{}\n", author); } if let Some(about) = self.about { - println!("{}", about); + print!("{}\n", about); } - println!(""); - self.print_usage(false, None); + print!("\n"); + + print!("{}", self.create_usage(None)); + if flags || opts || pos || subcmds { - println!(""); + print!("\n"); } let tab = " "; if flags { - println!(""); - println!("FLAGS:"); + print!("\nFLAGS:\n"); for v in self.flags.values() { - println!("{}{}{}{}",tab, - if let Some(s) = v.short{format!("-{}",s)}else{tab.to_owned()}, - if let Some(l) = v.long { - format!("{}--{}{}", - if v.short.is_some() { ", " } else {""}, - l, - self.get_spaces((longest_flag + 4) - (v.long.unwrap().len() + 2))) - } else { - // 6 is tab (4) + -- (2) - self.get_spaces(longest_flag + 6).to_owned() - }, - v.help.unwrap_or(tab) ); + print!("{}{}{}{}\n",tab, + if let Some(s) = v.short{format!("-{}",s)}else{tab.to_owned()}, + if let Some(l) = v.long { + format!("{}--{}{}", + if v.short.is_some() { ", " } else {""}, + l, + self.get_spaces((longest_flag + 4) - (v.long.unwrap().len() + 2))) + } else { + // 6 is tab (4) + -- (2) + self.get_spaces(longest_flag + 6).to_owned() + }, + v.help.unwrap_or(tab) + ); } } if opts { - println!(""); - println!("OPTIONS:"); + print!("\nOPTIONS:\n"); for v in self.opts.values() { // if it supports multiple we add '...' i.e. 3 to the name length - println!("{}{}{}{}{}{}",tab, - if let Some(s) = v.short{format!("-{}",s)}else{tab.to_owned()}, - if let Some(l) = v.long { - format!("{}--{}", - if v.short.is_some() {", "} else {""},l) + print!("{}{}{}{}{}{}\n",tab, + if let Some(s) = v.short{format!("-{}",s)}else{tab.to_owned()}, + if let Some(l) = v.long { + format!("{}--{}", + if v.short.is_some() {", "} else {""},l) + } else { + "".to_owned() + }, + format!("{}", + if let Some(ref vec) = v.val_names { + vec.iter().fold(String::new(), |acc, s| { + acc + &format!(" <{}>", s)[..] + }) + } else if let Some(num) = v.num_vals { + (0..num).fold(String::new(), |acc, _| { + acc + &format!(" <{}>", v.name)[..] + }) } else { - "".to_owned() + format!(" <{}>{}", v.name, if v.multiple{"..."} else {""}) + }), + if v.long.is_some() { + self.get_spaces( + (longest_opt + 4) - (v.to_string().len()) + ) + } else { + // 8 = tab + '-a, '.len() + self.get_spaces((longest_opt + 9) - (v.to_string().len())) }, - format!("{}", - if let Some(ref vec) = v.val_names { - vec.iter().fold(String::new(), |acc, s| { - acc + &format!(" <{}>", s)[..] - }) - } else if let Some(num) = v.num_vals { - (0..num).fold(String::new(), |acc, _| { - acc + &format!(" <{}>", v.name)[..] - }) - } else { - format!(" <{}>{}", v.name, if v.multiple{"..."} else {""}) - }), - if v.long.is_some() { - self.get_spaces( - (longest_opt + 4) - (v.to_string().len()) - ) - } else { - // 8 = tab + '-a, '.len() - self.get_spaces((longest_opt + 9) - (v.to_string().len())) - }, - get_help!(v) ); + get_help!(v) + ); } } if pos { - println!(""); - println!("ARGS:"); + print!("\nARGS:\n"); for v in self.positionals_idx.values() { let mult = if v.multiple { 3 } else { 0 }; - println!("{}{}{}{}",tab, + print!("{}{}{}{}\n",tab, if v.multiple {format!("{}...",v.name)} else {v.name.to_owned()}, self.get_spaces((longest_pos + 4) - (v.name.len() + mult)), - get_help!(v)); + get_help!(v) + ); } } if subcmds { - println!(""); - println!("SUBCOMMANDS:"); + print!("\nSUBCOMMANDS:\n"); for sc in self.subcommands.values() { - println!("{}{}{}{}",tab, - sc.name, - self.get_spaces((longest_sc + 4) - (sc.name.len())), - if let Some(a) = sc.about {a} else {tab} ); + print!("{}{}{}{}\n",tab, + sc.name, + self.get_spaces((longest_sc + 4) - (sc.name.len())), + if let Some(a) = sc.about {a} else {tab} + ); } } if let Some(h) = self.more_help { - println!(""); - println!("{}", h); + print!("\n{}", h); } + println!(""); + self.exit(0); } @@ -1413,19 +1416,23 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // This is legacy from before std::process::exit() and may be removed evenutally fn exit(&self, status: i32) { if self.wait_on_error { - println!("\nPress [ENTER] / [RETURN] to continue..."); + wlnerr!("\nPress [ENTER] / [RETURN] to continue..."); let mut s = String::new(); let i = io::stdin(); i.lock().read_line(&mut s).unwrap(); } - process::exit(status); + ::std::process::exit(status); } - // Reports and error to the users screen along with an optional usage statement and quits - fn report_error(&self, msg: String, usage: bool, quit: bool, matches: Option>) { - println!("{} {}\n", Format::Error("error:"), msg); - if usage { self.print_usage(true, matches); } - if quit { self.exit(1); } + // Reports and error to stderr along with an optional usage statement and optionally quits + fn report_error(&self, msg: String, quit: bool, matches: Option>) { + wlnerr!("{} {}\n\n{}\n\nFor more information try {}", + Format::Error(&format!("error:")[..]), + msg, + self.create_usage(matches), + Format::Good("--help") + ); + if quit { self.exit(1); } } /// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls @@ -1559,7 +1566,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ acc + &format!(" {}",name)[..] })), suffix.0), - true, true, Some(matches.args.keys().map(|k| *k).collect())); } @@ -1603,7 +1609,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Warning(opt.to_string()), Format::Good(vals.len().to_string())), true, - true, Some( matches.args.keys().map(|k| *k).collect() ) @@ -1619,7 +1624,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("The argument '{}' does not allow empty \ values, but one was found.", Format::Warning(opt.to_string())), true, - true, Some(matches.args.keys() .map(|k| *k).collect())); } @@ -1662,7 +1666,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ format!("The argument '{}' requires a value but none was supplied", Format::Warning(o.to_string())), true, - true, Some(matches.args.keys().map(|k| *k).collect() ) ); } } @@ -1702,7 +1705,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Good("--"), arg_slice), true, - true, None); } } @@ -1713,7 +1715,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Warning(&arg), self.bin_name.clone().unwrap_or(self.name.clone())), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } // If we find that an argument requires a positiona, we need to update all the @@ -1730,7 +1731,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ arguments".to_owned() }), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } @@ -1754,7 +1754,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Warning(&arg), Format::Warning(p.to_string())), true, - true, Some(matches.args.keys() .map(|k| *k).collect())); } @@ -1766,7 +1765,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("The argument '{}' does not allow empty \ values, but one was found.", Format::Warning(p.to_string())), true, - true, Some(matches.args.keys() .map(|k| *k).collect())); } @@ -1790,7 +1788,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("The argument '{}' does not allow empty \ values, but one was found.", Format::Warning(p.to_string())), true, - true, Some(matches.args.keys() .map(|k| *k).collect())); } @@ -1826,7 +1823,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ expecting any", Format::Warning(&arg), self.bin_name.clone().unwrap_or(self.name.clone())), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } @@ -1844,7 +1840,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ format!("The argument '{}' requires a value but there wasn't any \ supplied", Format::Warning(o.to_string())), true, - true, Some(matches.args.keys().map(|k| *k).collect() ) ); } } @@ -1853,7 +1848,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ format!("The argument '{}' requires a value but none was supplied", Format::Warning(o.to_string())), true, - true, Some(matches.args.keys().map(|k| *k).collect() ) ); } else { @@ -1866,7 +1860,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .fold(String::new(), |acc, s| acc + &format!("\n\t'{}'", Format::Error(s.to_string()))[..])), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } else { @@ -1875,7 +1868,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Warning(format!("{}", self.positionals_idx.get( self.positionals_name.get(a).unwrap()).unwrap()))), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } @@ -1929,7 +1921,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("'{}' requires a subcommand but none was provided", Format::Warning(&bn[..])), if self.usage_str.is_some() { true } else { false }, - if self.usage_str.is_some() { true } else { false }, Some(matches.args.keys().map(|k| *k).collect())); println!("USAGE:\n\t{} [SUBCOMMAND]\n\nFor more information re-run with {} or \ @@ -1950,7 +1941,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .fold(String::new(), |acc, s| acc + &format!("\n\t'{}'", Format::Error(s))[..])), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } @@ -2068,7 +2058,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("The argument '{}' requires a value, but none was \ supplied", Format::Warning(format!("--{}", arg))), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } arg_val = Some(arg_vec[1].to_owned()); @@ -2083,7 +2072,8 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ matches.args.remove(v.name); self.report_error(format!("The argument '{}' cannot be used with one or more of \ the other specified arguments", Format::Warning(format!("--{}", arg))), - true, true, Some(matches.args.keys().map(|k| *k).collect())); + true, + Some(matches.args.keys().map(|k| *k).collect())); } if matches.args.contains_key(v.name) { @@ -2092,7 +2082,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ does not support multiple values", Format::Warning(format!("--{}", arg))), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } if let Some(ref p_vals) = v.possible_vals { @@ -2109,7 +2098,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("The argument '{}' does not allow empty \ values, but one was found.", Format::Warning(v.to_string())), true, - true, Some(matches.args.keys() .map(|k| *k).collect())); } @@ -2126,7 +2114,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("The argument '{}' does not allow empty \ values, but one was found.", Format::Warning(v.to_string())), true, - true, Some(matches.args.keys() .map(|k| *k).collect())); } @@ -2182,7 +2169,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ None => "one or more of the specified arguments".to_owned() }), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } @@ -2191,7 +2177,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("The argument '{}' was supplied more than once, but does \ not support multiple values", Format::Warning(v.to_string())), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } @@ -2272,7 +2257,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Warning(format!("--{}", arg)), suffix.0), true, - true, Some(matches.args.keys().map(|k| *k).collect())); unreachable!(); @@ -2289,7 +2273,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("The argument '{}' isn't valid", Format::Warning(format!("-{}", c))), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } @@ -2319,7 +2302,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ None => "one or more of the other specified arguments".to_owned() }), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } @@ -2329,7 +2311,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ does not support multiple values", Format::Warning(format!("-{}", arg))), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } else { @@ -2367,7 +2348,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.report_error(format!("The argument '{}' isn't valid", Format::Warning(format!("-{}", arg_c))), true, - true, Some(matches.args.keys().map(|k| *k).collect())); unreachable!(); @@ -2388,7 +2368,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ arguments".to_owned() }), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } @@ -2398,7 +2377,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ not support multiple values", Format::Warning(format!("-{}", arg))), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } @@ -2460,7 +2438,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ }, match self.blacklisted_from(name, matches) { Some(name) => format!("'{}'", Format::Warning(name)), None => "one or more of the other specified arguments".to_owned() - }), true, true, Some(matches.args.keys().map(|k| *k).collect())); + }), + true, + Some(matches.args.keys().map(|k| *k).collect())); } else if self.groups.contains_key(name) { for n in self.get_group_members_names(name) { if matches.args.contains_key(n) { @@ -2480,7 +2460,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } }), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } @@ -2512,7 +2491,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ ( f.multiple && ( vals.len() % num as usize) == 1) {"as"}else{"ere"}), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } @@ -2525,7 +2503,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } @@ -2538,7 +2515,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } @@ -2553,7 +2529,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } @@ -2566,7 +2541,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } @@ -2579,7 +2553,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), true, - true, Some(matches.args.keys().map(|k| *k).collect())); } } diff --git a/src/macros.rs b/src/macros.rs index a8b799bd76c..25fa7f6054b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,3 +1,41 @@ +// Convenience for writing to stderr thanks to https://github.com/BurntSushi +macro_rules! wlnerr( + ($($arg:tt)*) => ({ + use std::io::{Write, stderr}; + writeln!(&mut stderr(), $($arg)*).ok(); + }) +); +macro_rules! werr( + ($($arg:tt)*) => ({ + use std::io::{Write, stderr}; + write!(&mut stderr(), $($arg)*).ok(); + }) +); + +#[cfg(feature = "debug")] +macro_rules! debugln { + ($fmt:expr) => (println!(concat!("**DEBUG** ", $fmt))); + ($fmt:expr, $($arg:tt)*) => (println!(concat!("**DEBUG** ",$fmt), $($arg)*)); +} + +#[cfg(feature = "debug")] +macro_rules! debug { + ($fmt:expr) => (print!(concat!("**DEBUG** ", $fmt))); + ($fmt:expr, $($arg:tt)*) => (println!(concat!("**DEBUG** ",$fmt), $($arg)*)); +} + +#[cfg(not(feature = "debug"))] +macro_rules! debugln { + ($fmt:expr) => (); + ($fmt:expr, $($arg:tt)*) => (); +} + +#[cfg(not(feature = "debug"))] +macro_rules! debug { + ($fmt:expr) => (); + ($fmt:expr, $($arg:tt)*) => (); +} + // De-duplication macro used in src/app.rs macro_rules! get_help { ($opt:ident) => {