From 0bab8dc9ca5161d649685b197582f2b69abdcbb6 Mon Sep 17 00:00:00 2001 From: Zach Dziura Date: Tue, 9 Jun 2015 16:50:16 -0400 Subject: [PATCH] Finished the Tokens refactor Now I just need to test the damn thing... --- src/lib.rs | 86 +--------------------------------------- src/matches.rs | 68 ++++++++++++++++++++++++++----- src/token.rs | 29 +++++++++++--- src/usage.rs | 24 +++++++++++ src/{opts.rs => vars.rs} | 29 ++++++++++---- 5 files changed, 129 insertions(+), 107 deletions(-) create mode 100644 src/usage.rs rename src/{opts.rs => vars.rs} (71%) diff --git a/src/lib.rs b/src/lib.rs index a1c81a2..57a6e59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,87 +17,5 @@ pub mod errors; pub mod matches; -mod opts; -mod token; - -use std::env::Args; - -pub use errors::{Error, ErrorKind}; -pub use matches::Matches; -pub use lexer::{analyze, collect, Token}; -use opts::{opts, Opts}; - -pub fn parse(mut args: Args, options: &[&'static str]) -> Result { - let mut matches: Matches = Matches::new(); - - let tokens = match lexer::collect(options) { - Err(why) => return Err(why), - Ok(t) => t - }; - matches.tokens = tokens.clone(); - let mut opts: Opts = opts(&tokens); - - args.next(); // Remove the program name from the list of program arguments - - let mut next_arg = args.next(); - while next_arg.is_some() { - let mut current_arg = next_arg.unwrap(); - let mut arg_vec: Vec = Vec::new(); - - // 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); - } - } - - 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); - } - - next_arg = args.next(); - } - - match opts.arg_len() { - 0 => Ok(matches), - _ => Err(Error::new(ErrorKind::MissingArgument, opts.get_arg().unwrap())), - } -} - -pub fn help(tokens: &Vec, program_name: &str, program_desc: &str) { - println!("{} - {}", program_name, program_desc); - - for token in tokens.iter() { - println!("{}", token); - } -} +pub mod vars; +mod token; \ No newline at end of file diff --git a/src/matches.rs b/src/matches.rs index d8ba706..af850ca 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -17,21 +17,71 @@ use std::collections::HashMap; use std::collections::hash_map::Keys; +use std::env; + +use errors::{Error, ErrorKind}; +use vars::Vars; pub struct Matches { - matches: HashMap + matches: HashMap, + program_name: String } impl Matches { - pub fn new() -> Matches { - Matches { - matches: HashMap::new(), - tokens: Vec::new() - } - } + pub fn new(opts: &mut Vars) -> Result { + let mut args = env::args(); + let mut matches: HashMap = HashMap::new(); + let program_name = args.next().unwrap(); + + let mut next_arg = args.next(); + while next_arg.is_some() { + let mut current_arg = next_arg.unwrap(); + let mut arg_vec: Vec = Vec::new(); - pub fn insert(&mut self, arg: &str, value: &str) { - self.matches.insert(String::from(arg), String::from(value)); + // 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); + } + } + + 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 => return Err(Error::new(ErrorKind::MissingArgument, (*arg).clone())), + Some(a) => a + }; + + matches.insert(arg.clone(), current_arg); + } else { + matches.insert(arg.clone(), String::new()); + } + } else { + return Err(Error::new(ErrorKind::InvalidArgument, arg.clone())); + } + } + } else { // Probably a required arg + let arg_name: String = opts.get_arg().unwrap().clone(); + matches.insert(arg_name, current_arg); + } + + next_arg = args.next(); + } + + match opts.arg_len() { + 0 => Ok(Matches { matches: matches, program_name: program_name }), + _ => Err(Error::new(ErrorKind::MissingArgument, opts.get_arg().unwrap())), + } } pub fn get(&self, arg: &str) -> Option<&String> { diff --git a/src/token.rs b/src/token.rs index e45d1d6..a1a9c7d 100644 --- a/src/token.rs +++ b/src/token.rs @@ -25,7 +25,7 @@ pub struct Token { long_name: String, pub is_arg: bool, pub has_arg: bool, - is_group: bool, + pub is_group: bool, description: String, padding: usize } @@ -52,9 +52,9 @@ impl Token { } let option = if is_arg { - input[1..] + &input[1..] } else if has_arg { - input[..last_char] + &input[..last_char] } else { input }; @@ -102,7 +102,24 @@ impl Token { } } - pub fn adjust_padding(&self, padding: usize) { + pub fn len(&self) -> usize { + let short_name_empty = self.short_name.is_empty(); + let long_name_empty = self.long_name.is_empty(); + + let repr = if !short_name_empty && !long_name_empty { + format!("-{}, --{}", self.short_name, self.long_name) + } else if !short_name_empty && long_name_empty { + format!("-{}", self.short_name) + } else if short_name_empty && !long_name_empty { + format!("--{}", self.long_name) + } else { + String::new() + }; + + repr.len() + } + + pub fn adjust_padding(&mut self, padding: usize) { self.padding = padding; } } @@ -110,14 +127,14 @@ impl Token { impl Display for Token { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let mut spacing = String::new(); - for 0..self.padding { + for _ in 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) + format!(" -{}, --{}{} {}", self.short_name, self.long_name, spacing, self.description) }; write!(f, "{}", repr) diff --git a/src/usage.rs b/src/usage.rs new file mode 100644 index 0000000..27a9afd --- /dev/null +++ b/src/usage.rs @@ -0,0 +1,24 @@ +/* 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 vars::Vars; + +pub fn usage(vars: &Vars) { + for token in vars.tokens.iter() { + println!("{}", token); + } +} \ No newline at end of file diff --git a/src/opts.rs b/src/vars.rs similarity index 71% rename from src/opts.rs rename to src/vars.rs index 0e0da9d..c5f871e 100644 --- a/src/opts.rs +++ b/src/vars.rs @@ -17,21 +17,23 @@ use std::collections::HashMap; use std::collections::VecDeque; +use std::slice::Iter; use errors::Error; use token::Token; -pub struct Opts { +pub struct Vars { pub opts: HashMap, pub args: VecDeque, - tokens: Vec + pub tokens: Vec } -impl Opts { - pub fn new(options: &[&str]) -> Result { - let mut opts: Hashmap = HashMap::new(); +impl Vars { + pub fn new(options: &[&str]) -> Result { + let mut opts: HashMap = HashMap::new(); let mut args: VecDeque = VecDeque::new(); let mut tokens: Vec = Vec::new(); + let mut longest_token_len: usize = 0; for opt in options.iter() { let token = match Token::new(opt) { @@ -45,14 +47,25 @@ impl Opts { } else { opts.insert(String::from(token.name()), token.has_arg); } + + let token_len = token.len(); + if token_len > 0 { + if token_len > longest_token_len { + longest_token_len = token_len; + for t in tokens.iter_mut() { + let diff = longest_token_len - t.len(); + t.adjust_padding(diff); + } + } + } } tokens.push(token); } - opts.insert(String::from("-h", false)); - opts.insert(String::from("--help", false)); + opts.insert(String::from("-h"), false); + opts.insert(String::from("--help"), false); - Ok(Opts { + Ok(Vars { opts: opts, args: args, tokens: tokens