Implement Close command to close current DB.
* Closes existing open db and opens new in memory db
This commit is contained in:
parent
f3d39d4194
commit
45d00c43ac
4 changed files with 140 additions and 32 deletions
|
@ -8,12 +8,13 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// specific language governing permissions and limitations under the License.
|
// 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::combinator::{choice, try};
|
||||||
use combine::char::{alpha_num, space, string};
|
use combine::char::{space, string};
|
||||||
|
|
||||||
pub static HELP_COMMAND: &'static str = &"help";
|
pub static HELP_COMMAND: &'static str = &"help";
|
||||||
pub static OPEN_COMMAND: &'static str = &"open";
|
pub static OPEN_COMMAND: &'static str = &"open";
|
||||||
|
pub static CLOSE_COMMAND: &'static str = &"close";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
|
@ -21,6 +22,7 @@ pub enum Command {
|
||||||
Query(Vec<String>),
|
Query(Vec<String>),
|
||||||
Help(Vec<String>),
|
Help(Vec<String>),
|
||||||
Open(String),
|
Open(String),
|
||||||
|
Close,
|
||||||
Err(String),
|
Err(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,29 +31,50 @@ impl Command {
|
||||||
match self {
|
match self {
|
||||||
&Command::Query(_) |
|
&Command::Query(_) |
|
||||||
&Command::Transact(_) => false,
|
&Command::Transact(_) => false,
|
||||||
_ => true
|
&Command::Help(_) |
|
||||||
|
&Command::Open(_) |
|
||||||
|
&Command::Close |
|
||||||
|
&Command::Err(_) => true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn command(s: &str) -> Command {
|
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 help_parser = string(HELP_COMMAND)
|
||||||
let args: Vec<String> = x.1.iter().map(|v| v.iter().collect() ).collect();
|
.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)
|
Command::Help(args)
|
||||||
});
|
});
|
||||||
|
|
||||||
let open_parser = string(OPEN_COMMAND).and(space()).and(many1::<Vec<_>, _>(alpha_num()).and(eof())).map(|x| {
|
let open_parser = string(OPEN_COMMAND)
|
||||||
let arg: String = (x.1).0.iter().collect();
|
.and(skip_many(space()))
|
||||||
Command::Open(arg)
|
.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('.')
|
let close_parser = string(CLOSE_COMMAND)
|
||||||
.and(choice::<[&mut Parser<Input = _, Output = Command>; 2], _>
|
.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(help_parser),
|
||||||
&mut try(open_parser),]))
|
&mut try(open_parser),
|
||||||
|
&mut try(close_parser),]))
|
||||||
|
.skip(eof())
|
||||||
.parse(s)
|
.parse(s)
|
||||||
.map(|x| x.0)
|
.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)]
|
#[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]
|
#[test]
|
||||||
fn test_help_parser_no_args() {
|
fn test_help_parser_no_args() {
|
||||||
let input = ".help";
|
let input = ".help";
|
||||||
|
@ -102,7 +137,7 @@ mod tests {
|
||||||
let cmd = command(&input);
|
let cmd = command(&input);
|
||||||
match cmd {
|
match cmd {
|
||||||
Command::Err(message) => {
|
Command::Err(message) => {
|
||||||
assert_eq!(message, format!("Invalid command {:?}", input));
|
assert_eq!(message, "Unrecognized argument \"database2\"");
|
||||||
},
|
},
|
||||||
_ => assert!(false)
|
_ => 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]
|
#[test]
|
||||||
fn test_open_parser_no_args() {
|
fn test_open_parser_no_args() {
|
||||||
let input = ".open";
|
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]
|
#[test]
|
||||||
fn test_command_parser_no_dot() {
|
fn test_command_parser_no_dot() {
|
||||||
let input = "help command1 command2";
|
let input = "help command1 command2";
|
||||||
|
|
|
@ -28,9 +28,8 @@ pub enum InputResult {
|
||||||
More(Command),
|
More(Command),
|
||||||
/// End of file reached
|
/// End of file reached
|
||||||
Eof,
|
Eof,
|
||||||
/// Error while parsing input; a Rust parsing error will have printed out
|
/// Error while parsing input;
|
||||||
/// error messages and therefore contain no error message.
|
InputError(String),
|
||||||
InputError(Option<String>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads input from `stdin`
|
/// Reads input from `stdin`
|
||||||
|
|
|
@ -66,9 +66,7 @@ impl Repl {
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
InputError(err) => {
|
InputError(err) => {
|
||||||
if let Some(err) = err {
|
|
||||||
println!("{}", err);
|
println!("{}", err);
|
||||||
}
|
|
||||||
more = None;
|
more = None;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -82,6 +80,7 @@ impl Repl {
|
||||||
Command::Open(db) => {
|
Command::Open(db) => {
|
||||||
self.store.open(Some(db));
|
self.store.open(Some(db));
|
||||||
},
|
},
|
||||||
|
Command::Close => self.store.close(),
|
||||||
Command::Err(message) => println!("{}", message),
|
Command::Err(message) => println!("{}", message),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
|
@ -93,7 +92,10 @@ impl Repl {
|
||||||
println!(".{} - {}", cmd, msg);
|
println!(".{} - {}", cmd, msg);
|
||||||
}
|
}
|
||||||
} else {
|
} 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());
|
let msg = COMMAND_HELP.get(arg.as_str());
|
||||||
if msg.is_some() {
|
if msg.is_some() {
|
||||||
println!(".{} - {}", arg, msg.unwrap());
|
println!(".{} - {}", arg, msg.unwrap());
|
||||||
|
|
|
@ -18,28 +18,34 @@ use mentat::conn::Conn;
|
||||||
pub struct Store {
|
pub struct Store {
|
||||||
handle: rusqlite::Connection,
|
handle: rusqlite::Connection,
|
||||||
conn: Conn,
|
conn: Conn,
|
||||||
|
db_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_output_name(db_name: &String) -> 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 {
|
impl Store {
|
||||||
pub fn new(database: Option<String>) -> Store {
|
pub fn new(database: Option<String>) -> Store {
|
||||||
let db_name = database.unwrap_or("".to_string());
|
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.");
|
let conn = Conn::connect(&mut handle).expect("Couldn't open DB.");
|
||||||
println!("Database {:?} opened", output_name);
|
println!("Database {:?} opened", db_output_name(&db_name));
|
||||||
Store { handle, conn }
|
Store { handle, conn, db_name }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(&mut self, database: Option<String>) {
|
pub fn open(&mut self, database: Option<String>) {
|
||||||
let db_name = database.unwrap_or("".to_string());
|
self.db_name = database.unwrap_or("".to_string());
|
||||||
let output_name = db_output_name(&db_name);
|
self.handle = new_connection(&self.db_name).expect("Couldn't open conn.");
|
||||||
self.handle = new_connection(db_name).expect("Couldn't open conn.");
|
|
||||||
self.conn = Conn::connect(&mut self.handle).expect("Couldn't open DB.");
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue