From 45d00c43ac49e5b6a5af9b5cec745d1cf9b728a4 Mon Sep 17 00:00:00 2001 From: Emily Toop Date: Mon, 15 May 2017 11:46:57 +0100 Subject: [PATCH] Implement Close command to close current DB. * Closes existing open db and opens new in memory db --- tools/cli/src/mentat_cli/command_parser.rs | 133 ++++++++++++++++++--- tools/cli/src/mentat_cli/input.rs | 5 +- tools/cli/src/mentat_cli/repl.rs | 10 +- tools/cli/src/mentat_cli/store.rs | 24 ++-- 4 files changed, 140 insertions(+), 32 deletions(-) diff --git a/tools/cli/src/mentat_cli/command_parser.rs b/tools/cli/src/mentat_cli/command_parser.rs index bec63486..fbf7d077 100644 --- a/tools/cli/src/mentat_cli/command_parser.rs +++ b/tools/cli/src/mentat_cli/command_parser.rs @@ -8,12 +8,13 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -use combine::{eof, many, many1, sep_by, skip_many, token, Parser}; +use combine::{any, eof, many, many1, sep_by, skip_many, token, Parser}; use combine::combinator::{choice, try}; -use combine::char::{alpha_num, space, string}; +use combine::char::{space, string}; pub static HELP_COMMAND: &'static str = &"help"; pub static OPEN_COMMAND: &'static str = &"open"; +pub static CLOSE_COMMAND: &'static str = &"close"; #[derive(Clone, Debug, Eq, PartialEq)] pub enum Command { @@ -21,6 +22,7 @@ pub enum Command { Query(Vec), Help(Vec), Open(String), + Close, Err(String), } @@ -29,29 +31,50 @@ impl Command { match self { &Command::Query(_) | &Command::Transact(_) => false, - _ => true + &Command::Help(_) | + &Command::Open(_) | + &Command::Close | + &Command::Err(_) => true } } } pub fn command(s: &str) -> Command { - let help_parser = string(HELP_COMMAND).and(skip_many(space())).and(sep_by::, _, _>(many1::, _>(alpha_num()), token(' '))).map(|x| { - let args: Vec = x.1.iter().map(|v| v.iter().collect() ).collect(); - Command::Help(args) - }); + let help_parser = string(HELP_COMMAND) + .and(skip_many(space())) + .and(many::, _>(try(any()))) + .map(|x| { + let remainder: String = x.1.iter().collect(); + let args: Vec = remainder.split(" ").filter_map(|s| if s.is_empty() { None } else { Some(s.to_string())}).collect(); + Command::Help(args) + }); - let open_parser = string(OPEN_COMMAND).and(space()).and(many1::, _>(alpha_num()).and(eof())).map(|x| { - let arg: String = (x.1).0.iter().collect(); - Command::Open(arg) - }); + let open_parser = string(OPEN_COMMAND) + .and(skip_many(space())) + .and(many1::, _>(try(any()))) + .map(|x| { + let remainder: String = x.1.iter().collect(); + let args: Vec = remainder.split(" ").filter_map(|s| if s.is_empty() { None } else { Some(s.to_string())}).collect(); + if args.len() > 1 { + return Command::Err(format!("Unrecognized argument {:?}", (&args[1]).clone())); + } + Command::Open((&args[0]).clone()) + }); - token('.') - .and(choice::<[&mut Parser; 2], _> + let close_parser = string(CLOSE_COMMAND) + .and(skip_many(space())) + .map( |_| Command::Close ); + + skip_many(space()) + .and(token('.')) + .and(choice::<[&mut Parser; 3], _> ([&mut try(help_parser), - &mut try(open_parser),])) + &mut try(open_parser), + &mut try(close_parser),])) + .skip(eof()) .parse(s) .map(|x| x.0) - .unwrap_or(('0', Command::Err(format!("Invalid command {:?}", s)))).1 + .unwrap_or((((), '0'), Command::Err(format!("Invalid command {:?}", s)))).1 } #[cfg(test)] @@ -70,6 +93,18 @@ mod tests { } } + #[test] + fn test_help_parser_dot_arg() { + let input = ".help .command1"; + let cmd = command(&input); + match cmd { + Command::Help(args) => { + assert_eq!(args, vec![".command1"]); + }, + _ => assert!(false) + } + } + #[test] fn test_help_parser_no_args() { let input = ".help"; @@ -102,7 +137,7 @@ mod tests { let cmd = command(&input); match cmd { Command::Err(message) => { - assert_eq!(message, format!("Invalid command {:?}", input)); + assert_eq!(message, "Unrecognized argument \"database2\""); }, _ => assert!(false) } @@ -120,6 +155,30 @@ mod tests { } } + #[test] + fn test_open_parser_path_arg() { + let input = ".open /path/to/my.db"; + let cmd = command(&input); + match cmd { + Command::Open(arg) => { + assert_eq!(arg, "/path/to/my.db".to_string()); + }, + _ => assert!(false) + } + } + + #[test] + fn test_open_parser_file_arg() { + let input = ".open my.db"; + let cmd = command(&input); + match cmd { + Command::Open(arg) => { + assert_eq!(arg, "my.db".to_string()); + }, + _ => assert!(false) + } + } + #[test] fn test_open_parser_no_args() { let input = ".open"; @@ -132,6 +191,48 @@ mod tests { } } + #[test] + fn test_close_parser_with_args() { + let input = ".close arg1"; + let cmd = command(&input); + match cmd { + Command::Err(message) => { + assert_eq!(message, format!("Invalid command {:?}", input)); + }, + _ => assert!(false) + } + } + + #[test] + fn test_close_parser_no_args() { + let input = ".close"; + let cmd = command(&input); + match cmd { + Command::Close => assert!(true), + _ => assert!(false) + } + } + + #[test] + fn test_close_parser_no_args_trailing_whitespace() { + let input = ".close "; + let cmd = command(&input); + match cmd { + Command::Close => assert!(true), + _ => assert!(false) + } + } + + #[test] + fn test_parser_preceeding_trailing_whitespace() { + let input = " .close "; + let cmd = command(&input); + match cmd { + Command::Close => assert!(true), + _ => assert!(false) + } + } + #[test] fn test_command_parser_no_dot() { let input = "help command1 command2"; diff --git a/tools/cli/src/mentat_cli/input.rs b/tools/cli/src/mentat_cli/input.rs index 2a9a35f0..a03f2939 100644 --- a/tools/cli/src/mentat_cli/input.rs +++ b/tools/cli/src/mentat_cli/input.rs @@ -28,9 +28,8 @@ pub enum InputResult { More(Command), /// End of file reached Eof, - /// Error while parsing input; a Rust parsing error will have printed out - /// error messages and therefore contain no error message. - InputError(Option), + /// Error while parsing input; + InputError(String), } /// Reads input from `stdin` diff --git a/tools/cli/src/mentat_cli/repl.rs b/tools/cli/src/mentat_cli/repl.rs index 9292fd2a..fdc82503 100644 --- a/tools/cli/src/mentat_cli/repl.rs +++ b/tools/cli/src/mentat_cli/repl.rs @@ -66,9 +66,7 @@ impl Repl { break; }, InputError(err) => { - if let Some(err) = err { - println!("{}", err); - } + println!("{}", err); more = None; }, }; @@ -82,6 +80,7 @@ impl Repl { Command::Open(db) => { self.store.open(Some(db)); }, + Command::Close => self.store.close(), Command::Err(message) => println!("{}", message), _ => unimplemented!(), } @@ -93,7 +92,10 @@ impl Repl { println!(".{} - {}", cmd, msg); } } else { - for arg in args { + for mut arg in args { + if arg.chars().nth(0).unwrap() == '.' { + arg.remove(0); + } let msg = COMMAND_HELP.get(arg.as_str()); if msg.is_some() { println!(".{} - {}", arg, msg.unwrap()); diff --git a/tools/cli/src/mentat_cli/store.rs b/tools/cli/src/mentat_cli/store.rs index c54757df..7243e4db 100644 --- a/tools/cli/src/mentat_cli/store.rs +++ b/tools/cli/src/mentat_cli/store.rs @@ -18,28 +18,34 @@ use mentat::conn::Conn; pub struct Store { handle: rusqlite::Connection, conn: Conn, + db_name: String, } fn db_output_name(db_name: &String) -> String { - if db_name.is_empty() { "in memory db".to_string() } else { db_name.clone() } + if db_name.is_empty() { "in-memory db".to_string() } else { db_name.clone() } } impl Store { pub fn new(database: Option) -> Store { let db_name = database.unwrap_or("".to_string()); - let output_name = db_output_name(&db_name); - let mut handle = new_connection(db_name).expect("Couldn't open conn."); + let mut handle = new_connection(&db_name).expect("Couldn't open conn."); let conn = Conn::connect(&mut handle).expect("Couldn't open DB."); - println!("Database {:?} opened", output_name); - Store { handle, conn } + println!("Database {:?} opened", db_output_name(&db_name)); + Store { handle, conn, db_name } } pub fn open(&mut self, database: Option) { - let db_name = database.unwrap_or("".to_string()); - let output_name = db_output_name(&db_name); - self.handle = new_connection(db_name).expect("Couldn't open conn."); + self.db_name = database.unwrap_or("".to_string()); + self.handle = new_connection(&self.db_name).expect("Couldn't open conn."); self.conn = Conn::connect(&mut self.handle).expect("Couldn't open DB."); - println!("Database {:?} opened", output_name); + println!("Database {:?} opened", db_output_name(&self.db_name)); + } + + pub fn close(&mut self) { + self.handle = new_connection("").expect("Couldn't close conn."); + self.conn = Conn::connect(&mut self.handle).expect("Couldn't close DB."); + println!("Database {:?} closed", db_output_name(&self.db_name)); + self.db_name = "".to_string(); } }