Exit CLI (#457) (#484) r-rnewman

* Implement exit command for cli tool

* Address review comments r=rnewman

* Include exit commands in help
This commit is contained in:
Emily Toop 2017-06-22 12:52:43 +01:00 committed by GitHub
parent 46fc1615fb
commit ecc926086a
2 changed files with 87 additions and 20 deletions

View file

@ -13,7 +13,6 @@ use combine::{
eof,
look_ahead,
many1,
parser,
satisfy,
sep_end_by,
token,
@ -29,8 +28,6 @@ use combine::combinator::{
try
};
use combine::primitives::Consumed;
use errors as cli;
use edn;
@ -42,14 +39,17 @@ pub static LONG_QUERY_COMMAND: &'static str = &"query";
pub static SHORT_QUERY_COMMAND: &'static str = &"q";
pub static LONG_TRANSACT_COMMAND: &'static str = &"transact";
pub static SHORT_TRANSACT_COMMAND: &'static str = &"t";
pub static LONG_EXIT_COMMAND: &'static str = &"exit";
pub static SHORT_EXIT_COMMAND: &'static str = &"e";
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Command {
Transact(String),
Query(String),
Close,
Exit,
Help(Vec<String>),
Open(String),
Close,
Query(String),
Transact(String),
}
impl Command {
@ -65,7 +65,8 @@ impl Command {
},
&Command::Help(_) |
&Command::Open(_) |
&Command::Close => true
&Command::Close |
&Command::Exit => true
}
}
@ -86,6 +87,9 @@ impl Command {
&Command::Close => {
format!(".{}", CLOSE_COMMAND)
},
&Command::Exit => {
format!(".{}", LONG_EXIT_COMMAND)
},
}
}
}
@ -112,18 +116,29 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
}
Ok(Command::Open(args[0].clone()))
});
let no_arg_parser = || arguments()
.skip(spaces())
.skip(eof());
let close_parser = string(CLOSE_COMMAND)
.with(arguments())
.skip(spaces())
.skip(eof())
.with(no_arg_parser())
.map(|args| {
if args.len() > 0 {
if !args.is_empty() {
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
}
Ok(Command::Close)
});
let exit_parser = try(string(LONG_EXIT_COMMAND)).or(try(string(SHORT_EXIT_COMMAND)))
.with(no_arg_parser())
.map(|args| {
if !args.is_empty() {
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
}
Ok(Command::Exit)
});
let edn_arg_parser = || spaces()
.with(look_ahead(string("[").or(string("{")))
.with(many1::<Vec<_>, _>(try(any())))
@ -146,10 +161,11 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
spaces()
.skip(token('.'))
.with(choice::<[&mut Parser<Input = _, Output = Result<Command, cli::Error>>; 5], _>
.with(choice::<[&mut Parser<Input = _, Output = Result<Command, cli::Error>>; 6], _>
([&mut try(help_parser),
&mut try(open_parser),
&mut try(close_parser),
&mut try(exit_parser),
&mut try(query_parser),
&mut try(transact_parser)]))
.parse(s)
@ -294,6 +310,43 @@ mod tests {
}
}
#[test]
fn test_exit_parser_with_args() {
let input = ".exit arg1";
let err = command(&input).expect_err("Expected an error");
assert_eq!(err.to_string(), format!("Invalid command {:?}", input));
}
#[test]
fn test_exit_parser_no_args() {
let input = ".exit";
let cmd = command(&input).expect("Expected exit command");
match cmd {
Command::Exit => assert!(true),
_ => assert!(false)
}
}
#[test]
fn test_exit_parser_no_args_trailing_whitespace() {
let input = ".exit ";
let cmd = command(&input).expect("Expected exit command");
match cmd {
Command::Exit => assert!(true),
_ => assert!(false)
}
}
#[test]
fn test_exit_parser_short_command() {
let input = ".e";
let cmd = command(&input).expect("Expected exit command");
match cmd {
Command::Exit => assert!(true),
_ => assert!(false)
}
}
#[test]
fn test_query_parser_complete_edn() {
let input = ".q [:find ?x :where [?x foo/bar ?y]]";

View file

@ -8,7 +8,10 @@
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
use std::collections::HashMap;
use std::collections::HashMap;
use std::process;
use error_chain::ChainedError;
use mentat::query::QueryResults;
use mentat_core::TypedValue;
@ -21,6 +24,8 @@ use command_parser::{
SHORT_QUERY_COMMAND,
LONG_TRANSACT_COMMAND,
SHORT_TRANSACT_COMMAND,
LONG_EXIT_COMMAND,
SHORT_EXIT_COMMAND,
};
use input::InputReader;
use input::InputResult::{
@ -37,6 +42,8 @@ use store::{
lazy_static! {
static ref COMMAND_HELP: HashMap<&'static str, &'static str> = {
let mut map = HashMap::new();
map.insert(LONG_EXIT_COMMAND, "Close the current database and exit the REPL.");
map.insert(SHORT_EXIT_COMMAND, "Shortcut for `.exit`. Close the current database and exit the REPL.");
map.insert(HELP_COMMAND, "Show help for commands.");
map.insert(OPEN_COMMAND, "Open a database at path.");
map.insert(LONG_QUERY_COMMAND, "Execute a query against the current open database.");
@ -103,18 +110,25 @@ impl Repl {
Err(e) => println!("{}", e.to_string())
};
},
Command::Close => {
let old_db_name = self.store.db_name.clone();
match self.store.close() {
Ok(_) => println!("Database {:?} closed", db_output_name(&old_db_name)),
Err(e) => println!("{}", e.to_string())
};
},
Command::Close => self.close(),
Command::Query(query) => self.execute_query(query),
Command::Transact(transaction) => self.execute_transact(transaction),
Command::Exit => {
self.close();
println!("Exiting...");
process::exit(0);
}
}
}
fn close(&mut self) {
let old_db_name = self.store.db_name.clone();
match self.store.close() {
Ok(_) => println!("Database {:?} closed", db_output_name(&old_db_name)),
Err(e) => println!("{}", e.display())
};
}
fn help_command(&self, args: Vec<String>) {
if args.is_empty() {
for (cmd, msg) in COMMAND_HELP.iter() {