From cc935c1850dbe23b02379cd073e30a35ec9ee14c Mon Sep 17 00:00:00 2001 From: Akshay Date: Mon, 3 Aug 2020 16:27:58 +0530 Subject: Revert "Refactor parsers" This reverts commit 76bdf45721d597922962e003568dcfdbcb548e63. --- src/command.rs | 500 ++++++++++++++------------------------------------------- 1 file changed, 122 insertions(+), 378 deletions(-) (limited to 'src') diff --git a/src/command.rs b/src/command.rs index 13ba194..9bf4e75 100644 --- a/src/command.rs +++ b/src/command.rs @@ -119,25 +119,9 @@ pub enum Command { Blank, } -#[derive(PartialEq, Debug)] -enum CommandName { - Add, - AddAuto, - MonthPrev, - MonthNext, - Delete, - TrackUp, - TrackDown, - Help, - Write, - Quit, - Blank, -} - -#[derive(PartialEq, Debug)] +#[derive(Debug)] pub enum CommandLineError { InvalidCommand(String), // command name - InvalidArg(u32), // position NotEnoughArgs(String, u32), // command name, required no. of args } @@ -147,7 +131,6 @@ impl fmt::Display for CommandLineError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { CommandLineError::InvalidCommand(s) => write!(f, "Invalid command: `{}`", s), - CommandLineError::InvalidArg(p) => write!(f, "Invalid argument at position {}", p), CommandLineError::NotEnoughArgs(s, n) => { write!(f, "Command `{}` requires atleast {} argument(s)!", s, n) } @@ -159,191 +142,77 @@ type Result = std::result::Result; impl Command { pub fn from_string>(input: P) -> Result { - let input_str = input.as_ref().trim(); - let parsed = parse_command_name(input_str); - if let Ok((command_name, rest)) = parsed { - match command_name { - CommandName::Add => return parse_add(rest), - CommandName::AddAuto => return parse_add_auto(rest), - CommandName::Delete => return parse_delete(rest), - CommandName::TrackUp => return parse_track_up(rest), - CommandName::TrackDown => return parse_track_down(rest), - CommandName::Help => return parse_help(rest), - CommandName::MonthPrev => return Ok(Command::MonthPrev), - CommandName::MonthNext => return Ok(Command::MonthNext), - CommandName::Quit => return Ok(Command::Quit), - CommandName::Write => return Ok(Command::Write), - CommandName::Blank => return Ok(Command::Blank), - } - } else { - return Err(parsed.err().unwrap()); + let mut strings: Vec<&str> = input.as_ref().trim().split(' ').collect(); + if strings.is_empty() { + return Ok(Command::Blank); } - } -} - -fn parse_command_name(input: &str) -> Result<(CommandName, &str)> { - let chars = input.trim().chars(); - let mut parsed_name = "".to_owned(); - let mut pos = 0; - for c in chars { - pos = pos + 1; - if c == ' ' { - break; + let first = strings.first().unwrap().to_string(); + let args: Vec = strings.iter_mut().skip(1).map(|s| s.to_string()).collect(); + let mut _add = |auto: bool, first: String| { + return parse_add(first, args.clone(), auto); + }; + + match first.as_ref() { + "add" | "a" => _add(false, first), + "add-auto" | "aa" => _add(true, first), + "delete" | "d" => { + if args.is_empty() { + return Err(CommandLineError::NotEnoughArgs(first, 1)); + } + return Ok(Command::Delete(args[0].to_string())); + } + "track-up" | "tup" => { + if args.is_empty() { + return Err(CommandLineError::NotEnoughArgs(first, 1)); + } + return Ok(Command::TrackUp(args[0].to_string())); + } + "track-down" | "tdown" => { + if args.is_empty() { + return Err(CommandLineError::NotEnoughArgs(first, 1)); + } + return Ok(Command::TrackDown(args[0].to_string())); + } + "h" | "?" | "help" => { + if args.is_empty() { + return Ok(Command::Help(None)); + } + return Ok(Command::Help(Some(args[0].to_string()))); + } + "mprev" | "month-prev" => return Ok(Command::MonthPrev), + "mnext" | "month-next" => return Ok(Command::MonthNext), + "q" | "quit" => return Ok(Command::Quit), + "w" | "write" => return Ok(Command::Write), + "" => return Ok(Command::Blank), + s => return Err(CommandLineError::InvalidCommand(s.into())), } - - parsed_name.push(c); - } - - match parsed_name.as_ref() { - "add" | "a" => Ok((CommandName::Add, &input[pos..])), - "add-auto" | "aa" => Ok((CommandName::AddAuto, &input[pos..])), - "delete" | "d" => Ok((CommandName::Delete, &input[pos..])), - "track-up" | "tup" => Ok((CommandName::TrackUp, &input[pos..])), - "track-down" | "tdown" => Ok((CommandName::TrackDown, &input[pos..])), - "h" | "?" | "help" => Ok((CommandName::Help, &input[pos..])), - "mprev" => Ok((CommandName::MonthPrev, &input[pos..])), - "mnext" => Ok((CommandName::MonthNext, &input[pos..])), - "quit" | "q" => Ok((CommandName::Quit, &input[pos..])), - "write" | "w" => Ok((CommandName::Write, &input[pos..])), - "" => Ok((CommandName::Blank, &input[pos..])), - _ => Err(CommandLineError::InvalidCommand(parsed_name)), } } -fn parse_name(input: &str) -> (String, &str) { - let chars = input.trim().chars(); - let mut name = "".to_owned(); - let mut pos = 0; - let mut parenthesis = false; +fn parse_add(verb: String, args: Vec, auto: bool) -> Result { + if args.is_empty() { + return Err(CommandLineError::NotEnoughArgs(verb, 1)); + } - for c in chars { - pos = pos + 1; - if c == '"' || c == '\"' { - if parenthesis { - return (name, &input[pos..]); + let mut pos = 1; + let mut acc = "".to_owned(); + let mut new_goal: Option = None; + for s1 in args { + if pos == 1 { + acc.push_str(&s1); + } else { + if let Ok(n) = s1.parse::() { + new_goal = Some(n); } else { - parenthesis = true; - continue; + acc.push(' '); + acc.push_str(&s1); } } - - if parenthesis { - name.push(c); - continue; - } - - if c == ' ' { - break; - } - - name.push(c); - } - - (name, &input[pos..]) -} - -fn parse_goal(input: &str) -> Option<(Option, &str)> { - let chars = input.trim().chars(); - let mut goal_string = "".to_owned(); - let mut pos = 0; - - if input.is_empty() { - return Some((None, input)); - } - - for c in chars { pos = pos + 1; - if c == ' ' { - break; - } - - goal_string.push(c); - } - - let parsed = goal_string.parse::(); - - if parsed.is_err() { - return None; - } - - if pos + 1 > input.len() { - return Some((parsed.ok(), "")); - } - - Some((parsed.ok(), &input[pos..])) -} - -fn parse_add(input: &str) -> Result { - let (name, rest) = parse_name(input); - - if name.is_empty() { - return Err(CommandLineError::NotEnoughArgs("add".to_owned(), 2)); - } - - let parsed_goal = parse_goal(rest); - - if parsed_goal.is_none() { - return Err(CommandLineError::InvalidArg(2)); - } - - Ok(Command::Add(name, parsed_goal.unwrap().0, false)) -} - -fn parse_add_auto(input: &str) -> Result { - let (name, rest) = parse_name(input); - - if name.is_empty() { - return Err(CommandLineError::NotEnoughArgs("add-auto".to_owned(), 2)); - } - - let parsed_goal = parse_goal(rest); - - if parsed_goal.is_none() { - return Err(CommandLineError::InvalidArg(2)); - } - - Ok(Command::Add(name, parsed_goal.unwrap().0, true)) -} - -fn parse_delete(input: &str) -> Result { - let (name, _) = parse_name(input); - - if name.is_empty() { - return Err(CommandLineError::NotEnoughArgs("delete".to_owned(), 1)); - } - - Ok(Command::Delete(name)) -} - -fn parse_track_up(input: &str) -> Result { - let (name, _) = parse_name(input); - - if name.is_empty() { - return Err(CommandLineError::NotEnoughArgs("track-up".to_owned(), 1)); - } - - Ok(Command::TrackUp(name)) -} - -fn parse_track_down(input: &str) -> Result { - let (name, _) = parse_name(input); - - if name.is_empty() { - return Err(CommandLineError::NotEnoughArgs("track-down".to_owned(), 1)); - } - - Ok(Command::TrackDown(name)) -} - -fn parse_help(input: &str) -> Result { - let (name, _) = parse_name(input); - - if name.is_empty() { - return Ok(Command::Help(None)); } - Ok(Command::Help(Some(name))) + return Ok(Command::Add(acc, new_goal, auto)); } #[cfg(test)] @@ -352,179 +221,113 @@ mod tests { #[test] fn parse_add_command() { - let inputs = ["add eat 2", "a eat 2"]; - - for input in inputs.iter() { - let result = Command::from_string(input); + let result = Command::from_string("add eat 2"); - assert!(result.is_ok()); - match result.unwrap() { - Command::Add(name, goal, auto) => { - assert_eq!(name, "eat"); - assert_eq!(goal.unwrap(), 2); - assert_eq!(auto, false); - } - _ => panic!(), + assert!(result.is_ok()); + match result.unwrap() { + Command::Add(name, goal, auto) => { + assert_eq!(name, "eat"); + assert_eq!(goal.unwrap(), 2); + assert_eq!(auto, false); } + _ => panic!(), } } #[test] fn parse_add_command_without_goal() { - let inputs = ["add eat", "a eat"]; + let result = Command::from_string("add eat"); - for input in inputs.iter() { - let result = Command::from_string(input); - - assert!(result.is_ok()); - match result.unwrap() { - Command::Add(name, goal, auto) => { - assert_eq!(name, "eat"); - assert!(goal.is_none()); - assert_eq!(auto, false); - } - _ => panic!(), + assert!(result.is_ok()); + match result.unwrap() { + Command::Add(name, goal, auto) => { + assert_eq!(name, "eat"); + assert!(goal.is_none()); + assert_eq!(auto, false); } + _ => panic!(), } } - #[test] + // #[test] fn parse_add_command_with_long_name() { - let inputs = ["add \"eat healthy\" 5", "a \"eat healthy\" 5"]; - - for input in inputs.iter() { - let result = Command::from_string(input); + let result = Command::from_string("add \"eat healthy\" 5"); - assert!(result.is_ok()); - match result.unwrap() { - Command::Add(name, goal, auto) => { - assert_eq!(name, "eat healthy"); - assert_eq!(goal.unwrap(), 5); - assert_eq!(auto, false); - } - _ => panic!(), + assert!(result.is_ok()); + match result.unwrap() { + Command::Add(name, goal, auto) => { + assert_eq!(name, "eat healthy"); + assert_eq!(goal.unwrap(), 5); + assert_eq!(auto, false); } + _ => panic!(), } } #[test] fn parse_add_auto_command() { - let inputs = ["add-auto eat 2", "aa eat 2"]; + let result = Command::from_string("add-auto eat 2"); - for input in inputs.iter() { - let result = Command::from_string(input); - - assert!(result.is_ok()); - match result.unwrap() { - Command::Add(name, goal, auto) => { - assert_eq!(name, "eat"); - assert_eq!(goal.unwrap(), 2); - assert_eq!(auto, true); - } - _ => panic!(), + assert!(result.is_ok()); + match result.unwrap() { + Command::Add(name, goal, auto) => { + assert_eq!(name, "eat"); + assert_eq!(goal.unwrap(), 2); + assert_eq!(auto, true); } + _ => panic!(), } } #[test] fn parse_delete_command() { - let inputs = ["delete eat", "d eat"]; - - for input in inputs.iter() { - let result = Command::from_string(input); + let result = Command::from_string("delete eat"); - assert!(result.is_ok()); - match result.unwrap() { - Command::Delete(name) => { - assert_eq!(name, "eat"); - } - _ => panic!(), - } - } - } - - #[test] - fn parse_delete_long_name_command() { - let inputs = ["delete \"eat healthy\"", "d \"eat healthy\""]; - - for input in inputs.iter() { - let result = Command::from_string(input); - - assert!(result.is_ok()); - match result.unwrap() { - Command::Delete(name) => { - assert_eq!(name, "eat healthy"); - } - _ => panic!(), + assert!(result.is_ok()); + match result.unwrap() { + Command::Delete(name) => { + assert_eq!(name, "eat"); } + _ => panic!(), } } #[test] fn parse_track_up_command() { - let inputs = ["track-up eat", "tup eat"]; - - for input in inputs.iter() { - let result = Command::from_string(input); + let result = Command::from_string("track-up eat"); - assert!(result.is_ok()); - match result.unwrap() { - Command::TrackUp(name) => { - assert_eq!(name, "eat"); - } - _ => panic!(), + assert!(result.is_ok()); + match result.unwrap() { + Command::TrackUp(name) => { + assert_eq!(name, "eat"); } + _ => panic!(), } } #[test] fn parse_track_down_command() { - let inputs = ["track-down eat", "tdown eat"]; - - for input in inputs.iter() { - let result = Command::from_string(input); + let result = Command::from_string("track-down eat"); - assert!(result.is_ok()); - match result.unwrap() { - Command::TrackDown(name) => { - assert_eq!(name, "eat"); - } - _ => panic!(), + assert!(result.is_ok()); + match result.unwrap() { + Command::TrackDown(name) => { + assert_eq!(name, "eat"); } + _ => panic!(), } } #[test] fn parse_help_command() { - let inputs = ["help add", "? add", "h add"]; - - for input in inputs.iter() { - let result = Command::from_string(input); - - assert!(result.is_ok()); - match result.unwrap() { - Command::Help(name) => { - assert_eq!(name.unwrap(), "add"); - } - _ => panic!(), - } - } - } + let result = Command::from_string("help add"); - #[test] - fn parse_help_global_command() { - let inputs = ["help", "?", "h"]; - - for input in inputs.iter() { - let result = Command::from_string(input); - - assert!(result.is_ok()); - match result.unwrap() { - Command::Help(name) => { - assert!(name.is_none()); - } - _ => panic!(), + assert!(result.is_ok()); + match result.unwrap() { + Command::Help(name) => { + assert_eq!(name.unwrap(), "add"); } + _ => panic!(), } } @@ -546,26 +349,18 @@ mod tests { #[test] fn parse_quit_command() { - let inputs = ["q", "quit"]; + let result = Command::from_string("q"); - for input in inputs.iter() { - let result = Command::from_string(input); - - assert!(result.is_ok()); - assert_eq!(result.unwrap(), Command::Quit); - } + assert!(result.is_ok()); + assert_eq!(result.unwrap(), Command::Quit); } #[test] fn parse_write_command() { - let inputs = ["w", "write"]; + let result = Command::from_string("w"); - for input in inputs.iter() { - let result = Command::from_string(input); - - assert!(result.is_ok()); - assert_eq!(result.unwrap(), Command::Write); - } + assert!(result.is_ok()); + assert_eq!(result.unwrap(), Command::Write); } #[test] @@ -575,55 +370,4 @@ mod tests { assert!(result.is_ok()); assert_eq!(result.unwrap(), Command::Blank); } - - #[test] - fn parse_error_invalid_command() { - let input = "non-existing-command".to_owned(); - let result = Command::from_string(&input); - - assert!(result.is_err()); - assert_eq!( - result.err().unwrap(), - CommandLineError::InvalidCommand(input) - ); - } - - #[test] - fn parse_error_not_enough_args() { - let test_cases = [ - ("add".to_owned(), "add".to_owned(), 2), - ("add-auto".to_owned(), "add-auto".to_owned(), 2), - ("delete".to_owned(), "delete".to_owned(), 1), - ("track-up".to_owned(), "track-up".to_owned(), 1), - ("track-down".to_owned(), "track-down".to_owned(), 1), - ]; - - for test_case in test_cases.iter() { - let result = Command::from_string(&test_case.0); - - assert!(result.is_err()); - assert_eq!( - result.err().unwrap(), - CommandLineError::NotEnoughArgs(test_case.1.clone(), test_case.2) - ); - } - } - - #[test] - fn parse_error_invalid_arg() { - let test_cases = [ - ("add habit n".to_owned(), 2), - ("add-auto habit n".to_owned(), 2), - ]; - - for test_case in test_cases.iter() { - let result = Command::from_string(&test_case.0); - - assert!(result.is_err()); - assert_eq!( - result.err().unwrap(), - CommandLineError::InvalidArg(test_case.1) - ); - } - } } -- cgit v1.2.3