X-Git-Url: https://harishankar.org/repos/?p=evpf.git;a=blobdiff_plain;f=src%2Fmain.rs;h=51ea94eb5e681337e5b6ecb386c8f81771fcb528;hp=bab1e521d17a5e4f2e794d645f851c2b448c0d01;hb=HEAD;hpb=ac6c2056e35e042ada5f88580329bc2ca51e72ff diff --git a/src/main.rs b/src/main.rs index bab1e52..51ea94e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,27 @@ -use std::io; -use std::io::Write; use regex::Regex; -use ansi_term::Colour; -use ansi_term::Style; +use colored::*; +use std::env; +use rustyline::Editor; +use std::path::PathBuf; const ILLEGAL_EXP : &'static str = "Illegal Expression"; const ERR_PARSING_NOT_MATCH : &'static str = "Error parsing expression! \ Must be a number or operator (+,-,* or /)"; const ERR_POSTFIX_INCOMPLETE : &'static str = "Postfix expression incomplete!"; -const ERR_FLUSHING : &'static str = "Error flushing!"; -const ERR_READING_LINE : &'static str = "Error reading line!"; const HELP_TEXT : [&str ; 4] = - ["Type an expression in postfix style to evaluate.", + ["Type a postfix expression to evaluate.", "Example: 4 5 + 12 -", "Supported operators: +, -, *, /", "Type q, Q to quit" ]; + +const HOMEDIR_NOT_FOUND : &'static str = "User home directory not found \ + (missing environment?)."; + +const HISTORY_FILE_NOT_FOUND : &'static str = "History file not found."; + +const SAVE_HISTORY_ERROR : &'static str = "Unable to save command history!"; -const RESULT : &'static str = "Result"; const ERROR : &'static str = "Error"; const ERROR_HELP : &'static str = "Type ? or h or H for help"; @@ -133,20 +137,86 @@ fn evaluate (expr : &str, match_num : ®ex::Regex) -> Result { } } -fn main() { +// Single command mode - command line arguments mode - evaluate the expression +// given in the command line and quit +fn run_command_line (args : &Vec, match_num : ®ex::Regex) { + let mut expr = String::new (); + let mut i = 0; + // create the expression string to evaluate + for arg in args.iter() { + if i > 0 { + expr.push_str (&arg); + expr.push_str (" "); + } + i += 1; + } + // evaluate the result + let res = evaluate (&expr, &match_num); + // if Result is OK then print the result in green + if res.is_ok () { + let restxt = format! ("{}", res.unwrap()); + println! ("{}", restxt.green ()); + } else { + // print the error in purple + let errtxt = format! ("{}: {}", ERROR, + res.unwrap_err()); + eprintln! ("{}", errtxt.purple ()); + } +} + +// get the history file name as string - home dir + .evpfhistory +fn get_history_file () -> Result { + // get the environment variable HOME + let home_path = env::var ("HOME"); + // if not found, return an error + if home_path.is_err () { + return Err (HOMEDIR_NOT_FOUND.to_string()); + } + // build the path for the history file i.e. homedir + .evpfhistory in + // platform independent way + let mut hist_file = PathBuf::new (); + hist_file.push (home_path.unwrap()); + hist_file.push (".evpfhistory"); + + // if cannot convert to string return error + if hist_file.to_str ().is_none () { + return Err (HOMEDIR_NOT_FOUND.to_string()); + } + + // return the history file path as a string + let hist_file_path = String::from (hist_file.to_str().unwrap()); + return Ok (hist_file_path); +} + +// Interactive mode - display the prompt and evaluate expressions entered into +// the prompt - until user quits +fn run_interactive_mode (match_num : ®ex::Regex) { // get a line from input and evaluate it let mut expr = String::new (); - // regular expression to match a number - let match_num = Regex::new (r"^\d+?\.*?\d*?$").unwrap (); + + let mut rl = Editor::<()>::new (); + + // load the history file + let hist_file = get_history_file (); + // if unable to load the history file, display the appropriate error + if hist_file.is_err () { + eprintln! ("{}", &hist_file.unwrap_err()); + } else { + if rl.load_history (&hist_file.unwrap ()).is_err () { + eprintln! ("{}", HISTORY_FILE_NOT_FOUND.purple()); + } + } // loop until a blank line is received loop { - expr.clear (); - print!("{}", Style::new().bold().paint("evpf>")); - io::stdout().flush ().expect (ERR_FLUSHING); - // read a line of text - io::stdin().read_line (&mut expr).expect (ERR_READING_LINE); - // trim the text - let expr = expr.trim (); + expr.clear (); + + let line = rl.readline ("evpf> "); + if line.is_err () { + break; + } + let hist = line.unwrap (); + rl.add_history_entry (&hist); + expr.push_str (&hist); if expr == "q" || expr == "Q" { // quit if the expression is q or Qs @@ -154,7 +224,7 @@ fn main() { } else if expr == "?" || expr == "h" || expr == "H" { // display help text for text in HELP_TEXT.iter() { - println! ("{}", Colour::Cyan.paint(*text)); + println! ("{}", text.cyan() ); } continue; @@ -168,15 +238,43 @@ fn main() { // if Result is OK then print the result in green if res.is_ok () { - let restxt = format! ("{}: {}", RESULT, - res.unwrap()); - println! ("{}", Colour::Green.paint (restxt)); + let restxt = format! ("{}", res.unwrap()); + println! ("{}", restxt.green ()); } else { // print the error in purple let errtxt = format! ("{}: {}", ERROR, res.unwrap_err()); - eprintln! ("{}", Colour::Purple.paint (errtxt)); - eprintln! ("{}", Colour::Purple.paint (ERROR_HELP)); + eprintln! ("{}", errtxt.purple()); + eprintln! ("{}", ERROR_HELP.purple()); + } + } + // save the history + let hist_file = get_history_file (); + if ! hist_file.is_err () { + if rl.save_history (&hist_file.unwrap()).is_err () { + eprintln! ("{}", SAVE_HISTORY_ERROR); } + } else { + eprintln! ("{}", &hist_file.unwrap_err()); + } +} + +fn main() { + // collect the command line arguments - if any + let args : Vec = env::args().collect (); + // regular expression to match a number + let match_num = Regex::new (r"^\-?\d+?\.*?\d*?$").unwrap (); + + if args.len () > 1 { + // if arguments are provided run in command line mode - i.e. print the + // result and exit + run_command_line (&args, &match_num); + + } else { + // if arguments are not provided run in interactive mode - + // display a prompt and get the expression + // repeat until the user quits + run_interactive_mode (&match_num); + } }