diff --git a/src/lexer.rs b/src/lexer.rs
deleted file mode 100644
index 823ed93..0000000
--- a/src/lexer.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-/* Pirate - A command-line arrrrguments parser, written in Rust.
- * Copyright (C) 2015 Zachary Dziura
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-use std::fmt::{self, Display, Formatter};
-
-use errors::{Error, ErrorKind};
-
-pub fn collect(input: &[&str]) -> Result, Error> {
- let mut vector: Vec = Vec::new();
- for item in input.iter() {
- match analyze(item) {
- Err(why) => return Err(why),
- Ok(item) => vector.push(item)
- }
- }
-
- Ok(vector)
-}
-
-pub fn analyze(input: &str) -> Result {
- let mut token = Token::new();
-
- token.is_arg = match &input[..1] {
- ":" => true,
- _ => false
- };
-
- token.has_arg = match &input[(input.len() - 1)..] {
- ":" => true,
- _ => false
- };
-
- if token.is_arg && token.has_arg {
- return Err(Error::new(ErrorKind::OptionFormat, String::from(input)));
- }
-
- let option = if token.is_arg {
- &input[1..]
- } else if token.has_arg {
- &input[..(input.len() - 1)]
- } else {
- input
- };
-
- let mut current_stage = AnalysisStage::ShortName;
- for c in option.chars() {
- match c {
- '/' => current_stage = AnalysisStage::LongName,
- '(' => current_stage = AnalysisStage::Description,
- ')' => (),
- _ => {
- match current_stage {
- AnalysisStage::ShortName => token.short_name.push(c),
- AnalysisStage::LongName => token.long_name.push(c),
- AnalysisStage::Description => token.description.push(c)
- }
- }
- }
- }
-
- if token.short_name.is_empty() && token.long_name.is_empty() {
- token.is_group = true;
- }
-
- Ok(token)
-}
-
-enum AnalysisStage {
- ShortName,
- LongName,
- Description
-}
-
-#[derive(Clone)]
-pub struct Token {
- short_name: String,
- long_name: String,
- pub is_arg: bool,
- pub has_arg: bool,
- is_group: bool,
- description: String
-}
-
-impl Token {
- pub fn new() -> Token {
- Token {
- short_name: String::new(),
- long_name: String::new(),
- is_arg: false,
- has_arg: false,
- is_group: false,
- description: String::new()
- }
- }
-
- pub fn name(&self) -> &str {
- if !self.short_name.is_empty() {
- &self.short_name
- } else if !self.long_name.is_empty() {
- &self.long_name
- } else {
- ""
- }
- }
-
- pub fn fmt_with_padding(&self, padding: usize) -> String {
- let mut name = format!("-{}, --{}", self.short_name, self.long_name);
-
- for _ in 0..padding {
- name.push(' ');
- }
-
- name
- }
-}
-
-impl Display for Token {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- let repr = format!("-{}, --{} {}", self.short_name, self.long_name, self.description);
- write!(f, "{}", repr)
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 76908b8..060c0ee 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -25,7 +25,7 @@ use std::env::Args;
pub use errors::{Error, ErrorKind};
pub use matches::Matches;
pub use lexer::{analyze, collect, Token};
-use opts::Opts;
+use opts::{opts, Opts};
pub fn parse(mut args: Args, options: &[&'static str]) -> Result {
let mut matches: Matches = Matches::new();
@@ -42,36 +42,44 @@ pub fn parse(mut args: Args, options: &[&'static str]) -> Result
let mut next_arg = args.next();
while next_arg.is_some() {
let mut current_arg = next_arg.unwrap();
- let arg: String;
+ let mut arg_vec: Vec = Vec::new();
- if ¤t_arg[..1] == "-" { // Probably a opt
- if current_arg.len() == 2 { // Short form opt
- arg = String::from(¤t_arg[1..]);
- } else { // Assuming it's a long form opt
- //TODO: Handle cases where it may be a opt group
- arg = String::from(¤t_arg[2..]);
- }
-
- if opts.contains_opt(&arg) {
- let has_arg: bool = *opts.get_opt(&arg).unwrap();
-
- if has_arg {
- // NOTE: The corresponding arg MUST be immediately following
- current_arg = match args.next() {
- None => {
- return Err(Error::new(ErrorKind::MissingArgument, arg));
- },
- Some(a) => a
- };
-
- matches.insert(&arg, ¤t_arg);
- } else {
- matches.insert(&arg, "");
+ // Determine if current opt is in short, long, or arg form
+ if ¤t_arg[..1] == "-" {
+ if ¤t_arg[..2] == "--" { // Long form opt
+ arg_vec.push(String::from(¤t_arg[2..]));
+ } else { // Short form opt
+ // Assuming it's a group of short-form opts; e.g. tar -xzf
+ for c in current_arg[1..].chars() {
+ let mut s = String::new();
+ s.push(c);
+ arg_vec.push(s);
}
- } else {
- return Err(Error::new(ErrorKind::InvalidArgument, arg));
}
+ for arg in arg_vec.iter() {
+ if opts.contains_opt(&arg) {
+ let has_arg: bool = *opts.get_opt(&arg).unwrap();
+
+ if has_arg {
+ // NOTE: The corresponding arg MUST be immediately following
+ current_arg = match args.next() {
+ None => {
+ let arg_ = (*arg).clone();
+ return Err(Error::new(ErrorKind::MissingArgument, arg_));
+ },
+ Some(a) => a
+ };
+
+ matches.insert(&arg, ¤t_arg);
+ } else {
+ matches.insert(&arg, "");
+ }
+ } else {
+ let arg_ = (*arg).clone();
+ return Err(Error::new(ErrorKind::InvalidArgument, arg_));
+ }
+ }
} else { // Probably a required arg
let arg_name: String = opts.get_arg().unwrap();
matches.insert(&arg_name, ¤t_arg);
@@ -86,20 +94,10 @@ pub fn parse(mut args: Args, options: &[&'static str]) -> Result
}
}
-fn opts(opts: &Vec) -> Opts {
- let mut options = Opts::new();
+pub fn help(tokens: &Vec, program_name: &str, program_desc: &str) {
+ println!("{} - {}", program_name, program_desc);
- for opt in opts.iter() {
- if opt.is_arg {
- options.insert_arg(opt.name());
- } else {
- options.insert_opt(opt.name(), opt.has_arg);
- }
+ for token in tokens.iter() {
+ println!("{}", token);
}
-
- // Push the obligatory "-h/--help" options
- options.insert_opt("h", false);
- options.insert_opt("help", false);
-
- options
}
diff --git a/src/matches.rs b/src/matches.rs
index 9056ba7..d8ba706 100644
--- a/src/matches.rs
+++ b/src/matches.rs
@@ -18,11 +18,8 @@
use std::collections::HashMap;
use std::collections::hash_map::Keys;
-use lexer;
-
pub struct Matches {
- matches: HashMap,
- pub tokens: Vec
+ matches: HashMap
}
impl Matches {
diff --git a/src/opts.rs b/src/opts.rs
index df042e1..3063cb0 100644
--- a/src/opts.rs
+++ b/src/opts.rs
@@ -18,21 +18,33 @@
use std::collections::HashMap;
use std::collections::VecDeque;
+use lexer;
+
pub struct Opts {
pub opts: HashMap,
pub args: VecDeque,
}
impl Opts {
- pub fn new() -> Opts {
- Opts {
- opts: HashMap::new(),
- args: VecDeque::new()
+ pub fn new(options: &[&'static str]) -> Opts {
+ let opts: Hashmap = HashMap::new();
+ let args: VecDeque = VecDeque::new();
+
+ for opt in opts.iter() {
+ if opt.is_arg {
+ args.push_back(String::from(opt.name()));
+ } else {
+ opts.insert(String::from(opt.name()), opt.has_arg);
+ }
+ }
+
+ opts.insert(String::from("-h", false));
+ opts.insert(String::from("--help", false));
+
+ Opts {
+ opts: opts,
+ args: args
}
- }
-
- pub fn insert_opt(&mut self, key: &str, value: bool) {
- self.opts.insert(String::from(key), value);
}
pub fn get_opt(&self, opt_name: &String) -> Option<&bool> {
@@ -43,10 +55,6 @@ impl Opts {
self.opts.contains_key(opt)
}
- pub fn insert_arg(&mut self, arg: &str) {
- self.args.push_back(String::from(arg));
- }
-
pub fn get_arg(&mut self) -> Option {
self.args.pop_front()
}
@@ -54,4 +62,4 @@ impl Opts {
pub fn arg_len(&self) -> usize {
self.args.len()
}
-}
+}
\ No newline at end of file
diff --git a/src/token.rs b/src/token.rs
new file mode 100644
index 0000000..d7d1043
--- /dev/null
+++ b/src/token.rs
@@ -0,0 +1,137 @@
+/* Pirate - A command-line arrrrguments parser, written in Rust.
+ * Copyright (C) 2015 Zachary Dziura
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+use std::fmt::{self, Display, Formatter};
+
+use errors::{Error, ErrorKind};
+
+#[derive(Clone)]
+pub struct Token {
+ short_name: String,
+ long_name: String,
+ pub is_arg: bool,
+ pub has_arg: bool,
+ is_group: bool,
+ description: String,
+ padding: usize
+}
+
+impl Token {
+ pub fn new(input: &str, padding: usize) -> Result {
+ let mut short_name = String::new();
+ let mut long_name = String::new();
+ let mut description = String::new();
+ let last_char = input.len() - 1;
+
+ let is_arg = match &input[..1] {
+ ":" => true,
+ _ => false
+ };
+
+ let has_arg = match &input[last_char..] {
+ ":" => true,
+ _ => false
+ };
+
+ if is_arg && has_arg {
+ return Err(Error::new(ErrorKind::OptionFormat, String::from(input)));
+ }
+
+ let option = if is_arg {
+ input[1..]
+ } else if has_arg {
+ input[..last_char]
+ } else {
+ input
+ };
+
+ let mut current_stage = AnalysisStage::ShortName;
+ for c in option.chars() {
+ match c {
+ '/' => current_stage = AnalysisStage::LongName,
+ '(' => current_stage = AnalysisStage::Description,
+ ')' => (),
+ _ => {
+ match current_stage {
+ AnalysisStage::ShortName => short_name.push(c),
+ AnalysisStage::LongName => long_name.push(c),
+ AnalysisStage::Description => description.push(c)
+ }
+ }
+ }
+ }
+
+ let is_group = if short_name.is_empty() && long_name.is_empty() {
+ true
+ } else {
+ false
+ };
+
+ Ok(Token {
+ short_name: short_name,
+ long_name: long_name,
+ is_arg: is_arg,
+ has_arg: has_arg,
+ is_group: is_group,
+ description: description,
+ padding: padding
+ })
+ }
+
+ pub fn name(&self) -> &str {
+ if !self.short_name.is_empty() {
+ &self.short_name
+ } else if !self.long_name.is_empty() {
+ &self.long_name
+ } else {
+ ""
+ }
+ }
+
+ pub fn fmt_with_padding(&self, padding: usize) -> String {
+ let mut name = format!("-{}, --{}", self.short_name, self.long_name);
+
+ for _ in 0..padding {
+ name.push(' ');
+ }
+
+ name
+ }
+}
+
+impl Display for Token {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ let mut spacing = String::new();
+ for 0..self.padding {
+ spacing.push(' ');
+ }
+
+ let repr = if self.is_group {
+ format!("{}:", self.description)
+ } else {
+ format!("-{}, --{}{}{}", self.short_name, self.long_name, spacing self.description)
+ };
+
+ write!(f, "{}", repr)
+ }
+}
+
+enum AnalysisStage {
+ ShortName,
+ LongName,
+ Description
+}
\ No newline at end of file