Implement Close command to close current DB.

* Closes existing open db and opens new in memory db
This commit is contained in:
Emily Toop 2017-05-15 11:46:57 +01:00
parent f3d39d4194
commit 45d00c43ac
4 changed files with 140 additions and 32 deletions

View file

@ -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<String>),
Help(Vec<String>),
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::<Vec<_>, _, _>(many1::<Vec<_>, _>(alpha_num()), token(' '))).map(|x| {
let args: Vec<String> = x.1.iter().map(|v| v.iter().collect() ).collect();
let help_parser = string(HELP_COMMAND)
.and(skip_many(space()))
.and(many::<Vec<_>, _>(try(any())))
.map(|x| {
let remainder: String = x.1.iter().collect();
let args: Vec<String> = 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::<Vec<_>, _>(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::<Vec<_>, _>(try(any())))
.map(|x| {
let remainder: String = x.1.iter().collect();
let args: Vec<String> = 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<Input = _, Output = Command>; 2], _>
let close_parser = string(CLOSE_COMMAND)
.and(skip_many(space()))
.map( |_| Command::Close );
skip_many(space())
.and(token('.'))
.and(choice::<[&mut Parser<Input = _, Output = Command>; 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";

View file

@ -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<String>),
/// Error while parsing input;
InputError(String),
}
/// Reads input from `stdin`

View file

@ -66,9 +66,7 @@ impl Repl {
break;
},
InputError(err) => {
if let Some(err) = 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());

View file

@ -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<String>) -> 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<String>) {
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();
}
}