Implement methods for adding command line arguments to the parser.

This commit is contained in:
Nathan Vegdahl 2022-03-27 12:27:32 -07:00
parent 3d198452c9
commit db277c7805

View File

@ -1,6 +1,8 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::env::args_os; use std::env::args_os;
use std::ops::RangeBounds;
/// A command line argument parser.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Parser { pub struct Parser {
flags: Vec<Flag>, flags: Vec<Flag>,
@ -12,17 +14,107 @@ pub struct Parser {
short_set: HashSet<String>, short_set: HashSet<String>,
} }
impl Parser {
pub fn new() -> Parser {
Parser {
flags: Vec::new(),
args: Vec::new(),
pos_args: Vec::new(),
long_set: HashSet::new(),
short_set: HashSet::new(),
}
}
/// Add a flag argument.
pub fn flag(&mut self, long: &str, short: Option<&str>, doc_string: &str) {
self.ensure_not_duplicate(long, short);
self.flags.push(Flag {
long: long.into(),
short: short.map(|s| s.into()),
doc_string: doc_string.into(),
});
}
/// Add a standard argument.
pub fn argument<R: RangeBounds<usize>>(
&mut self,
long: &str,
short: Option<&str>,
doc_string: &str,
value_name: &str,
acceptable_count: R,
) {
self.ensure_not_duplicate(long, short);
use std::ops::Bound;
let count_start: Option<usize> = match acceptable_count.start_bound() {
Bound::Included(&n) => Some(n),
Bound::Excluded(&n) => Some(n + 1),
Bound::Unbounded => None,
};
let count_end: Option<usize> = match acceptable_count.end_bound() {
Bound::Included(&n) => Some(n + 1),
Bound::Excluded(&n) => Some(n),
Bound::Unbounded => None,
};
self.args.push(Arg {
long: long.into(),
short: short.map(|s| s.into()),
acceptable_count: (count_start, count_end),
value_name: value_name.into(),
doc_string: doc_string.into(),
});
}
/// Add a positional argument.
///
/// There can only be one positional argument with `count == None`,
/// which represents any left over positional arguments after those
/// with explicit counts have been parsed.
pub fn positional_argument(
&mut self,
doc_string: &str,
value_name: &str,
count: Option<usize>,
) {
self.pos_args.push(PosArg {
count: count,
value_name: value_name.into(),
doc_string: doc_string.into(),
});
}
//----------------
fn ensure_not_duplicate(&mut self, long: &str, short: Option<&str>) {
if self.long_set.contains(long) {
panic!("Attempted to add duplicate long argument \"--{}\".", long);
}
self.long_set.insert(long.into());
if let Some(short) = short {
if self.short_set.contains(short) {
panic!("Attempted to add duplicate short argument \"-{}\".", short);
}
self.short_set.insert(short.into());
}
}
}
/// Parsed command line arguments.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Arguments { pub struct Arguments {
// TODO: allow OsString argument values. // TODO: allow OsString argument values.
/// Positional arguments, in the order passed. /// The flags that were passed, indexed by `long`.
pub positional: Vec<String>,
/// The flags that were passed.
pub flags: HashSet<String>, pub flags: HashSet<String>,
/// Non-positional arguments that were passed. /// Non-positional arguments that were passed, indexed by `long`.
pub args: HashMap<String, String>, pub args: HashMap<String, Vec<String>>,
/// Positional arguments, indexed by `value_name`.
pub positional: HashMap<String, Vec<String>>,
} }
//------------------------------------------------------------- //-------------------------------------------------------------
@ -30,7 +122,7 @@ pub struct Arguments {
/// Flag spec. /// Flag spec.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Flag { struct Flag {
long: Option<String>, long: String,
short: Option<String>, short: Option<String>,
// For documentation. // For documentation.
@ -40,9 +132,9 @@ struct Flag {
/// Argument spec. /// Argument spec.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Arg { struct Arg {
long: Option<String>, long: String,
short: Option<String>, short: Option<String>,
is_required: bool, acceptable_count: (Option<usize>, Option<usize>),
// For documentation. // For documentation.
value_name: String, value_name: String,
@ -52,7 +144,7 @@ struct Arg {
/// Positional argument spec. /// Positional argument spec.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct PosArg { struct PosArg {
is_required: bool, count: Option<usize>,
// For documentation. // For documentation.
value_name: String, value_name: String,