Accept and parse read commands
This commit is contained in:
parent
46fc1615fb
commit
66c402b205
3 changed files with 139 additions and 7 deletions
|
@ -42,6 +42,7 @@ pub static LONG_QUERY_COMMAND: &'static str = &"query";
|
||||||
pub static SHORT_QUERY_COMMAND: &'static str = &"q";
|
pub static SHORT_QUERY_COMMAND: &'static str = &"q";
|
||||||
pub static LONG_TRANSACT_COMMAND: &'static str = &"transact";
|
pub static LONG_TRANSACT_COMMAND: &'static str = &"transact";
|
||||||
pub static SHORT_TRANSACT_COMMAND: &'static str = &"t";
|
pub static SHORT_TRANSACT_COMMAND: &'static str = &"t";
|
||||||
|
pub static READ_COMMAND: &'static str = &"read";
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
|
@ -49,6 +50,7 @@ pub enum Command {
|
||||||
Query(String),
|
Query(String),
|
||||||
Help(Vec<String>),
|
Help(Vec<String>),
|
||||||
Open(String),
|
Open(String),
|
||||||
|
Read(Vec<String>),
|
||||||
Close,
|
Close,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +67,8 @@ impl Command {
|
||||||
},
|
},
|
||||||
&Command::Help(_) |
|
&Command::Help(_) |
|
||||||
&Command::Open(_) |
|
&Command::Open(_) |
|
||||||
&Command::Close => true
|
&Command::Close |
|
||||||
|
&Command::Read(_) => true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +89,9 @@ impl Command {
|
||||||
&Command::Close => {
|
&Command::Close => {
|
||||||
format!(".{}", CLOSE_COMMAND)
|
format!(".{}", CLOSE_COMMAND)
|
||||||
},
|
},
|
||||||
|
&Command::Read(ref args) => {
|
||||||
|
format!(".{} {:?}", READ_COMMAND, args)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,14 +150,43 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
Ok(Command::Transact(x))
|
Ok(Command::Transact(x))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let read_parser = string(READ_COMMAND)
|
||||||
|
.with(spaces())
|
||||||
|
.with(arguments())
|
||||||
|
.map(|args| {
|
||||||
|
// strip quotes from file paths.
|
||||||
|
let mut sargs = Vec::with_capacity(args.len());
|
||||||
|
for arg in args.iter() {
|
||||||
|
let start_char = arg.chars().nth(0);
|
||||||
|
match start_char {
|
||||||
|
Some('"') |
|
||||||
|
Some('\'') => {
|
||||||
|
let separator = start_char.unwrap();
|
||||||
|
if arg.ends_with(separator) {
|
||||||
|
sargs.push(arg.split(separator).collect::<Vec<&str>>().into_iter().collect());
|
||||||
|
} else {
|
||||||
|
return Err(cli::ErrorKind::CommandParse(format!("Unrecognized argument {}", arg)).into());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => sargs.push(arg.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("args: {:?}", sargs);
|
||||||
|
if args.len() == 0 {
|
||||||
|
return Err(cli::ErrorKind::CommandParse("Missing required argument".to_string()).into());
|
||||||
|
}
|
||||||
|
Ok(Command::Read(sargs.clone()))
|
||||||
|
});
|
||||||
|
|
||||||
spaces()
|
spaces()
|
||||||
.skip(token('.'))
|
.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(help_parser),
|
||||||
&mut try(open_parser),
|
&mut try(open_parser),
|
||||||
&mut try(close_parser),
|
&mut try(close_parser),
|
||||||
&mut try(query_parser),
|
&mut try(query_parser),
|
||||||
&mut try(transact_parser)]))
|
&mut try(transact_parser),
|
||||||
|
&mut try(read_parser)]))
|
||||||
.parse(s)
|
.parse(s)
|
||||||
.unwrap_or((Err(cli::ErrorKind::CommandParse(format!("Invalid command {:?}", s)).into()), "")).0
|
.unwrap_or((Err(cli::ErrorKind::CommandParse(format!("Invalid command {:?}", s)).into()), "")).0
|
||||||
}
|
}
|
||||||
|
@ -412,6 +447,89 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_parser_single_arg_no_quotes() {
|
||||||
|
let input = ".read arg1";
|
||||||
|
let cmd = command(&input).expect("Expected read command");
|
||||||
|
match cmd {
|
||||||
|
Command::Read(files) => assert_eq!(files, vec!["arg1"]),
|
||||||
|
_ => assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_parser_single_arg_quotes() {
|
||||||
|
let input = r#".read "arg1""#;
|
||||||
|
let cmd = command(&input).expect("Expected read command");
|
||||||
|
match cmd {
|
||||||
|
Command::Read(files) => assert_eq!(files, vec!["arg1"]),
|
||||||
|
_ => assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_parser_single_arg_file_extension() {
|
||||||
|
let input = ".read arg1.edn";
|
||||||
|
let cmd = command(&input).expect("Expected read command");
|
||||||
|
match cmd {
|
||||||
|
Command::Read(files) => assert_eq!(files, vec!["arg1.edn"]),
|
||||||
|
_ => assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_parser_single_arg_file_path() {
|
||||||
|
let input = ".read ~/path/to/data/arg1.edn";
|
||||||
|
let cmd = command(&input).expect("Expected read command");
|
||||||
|
match cmd {
|
||||||
|
Command::Read(files) => assert_eq!(files, vec!["~/path/to/data/arg1.edn"]),
|
||||||
|
_ => assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_parser_single_arg_file_path_quotes() {
|
||||||
|
let input = r#".read "~/path/to/data/arg1.edn""#;
|
||||||
|
let cmd = command(&input).expect("Expected read command");
|
||||||
|
match cmd {
|
||||||
|
Command::Read(files) => assert_eq!(files, vec!["~/path/to/data/arg1.edn"]),
|
||||||
|
_ => assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_read_parser_single_arg_bad_quotes() {
|
||||||
|
let input = r#".read "~/path/to/data/arg1.edn"#;
|
||||||
|
let err = command(&input).expect_err("Expected an error");
|
||||||
|
assert_eq!(err.to_string(), "Unrecognized argument \"~/path/to/data/arg1.edn");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_parser_multiple_args() {
|
||||||
|
let input = ".read ~/path/to/data/arg1.edn ~/path/to/schema/arg2.edn";
|
||||||
|
let cmd = command(&input).expect("Expected read command");
|
||||||
|
match cmd {
|
||||||
|
Command::Read(files) => assert_eq!(files, vec!["~/path/to/data/arg1.edn", "~/path/to/schema/arg2.edn"]),
|
||||||
|
_ => assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_parser_multiple_args_mix_quotes() {
|
||||||
|
let input = r#".read "~/path/to/data/arg1.edn" '~/path/to/schema/arg2.edn'"#;
|
||||||
|
let cmd = command(&input).expect("Expected read command");
|
||||||
|
match cmd {
|
||||||
|
Command::Read(files) => assert_eq!(files, vec!["~/path/to/data/arg1.edn", "~/path/to/schema/arg2.edn"]),
|
||||||
|
_ => assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_parser_no_args() {
|
||||||
|
let input = ".read";
|
||||||
|
let err = command(&input).expect_err("Expected an error");
|
||||||
|
assert_eq!(err.to_string(), "Missing required argument");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_command_parser_no_dot() {
|
fn test_command_parser_no_dot() {
|
||||||
let input = "help command1 command2";
|
let input = "help command1 command2";
|
||||||
|
|
|
@ -42,8 +42,9 @@ pub fn run() -> i32 {
|
||||||
|
|
||||||
opts.optopt("d", "", "The path to a database to open", "DATABASE");
|
opts.optopt("d", "", "The path to a database to open", "DATABASE");
|
||||||
opts.optflag("h", "help", "Print this help message and exit");
|
opts.optflag("h", "help", "Print this help message and exit");
|
||||||
opts.optmulti("q", "query", "Execute a query on startup. Queries are executed after any transacts.", "QUERY");
|
opts.optopt("q", "query", "Execute a query on startup.", "QUERY");
|
||||||
opts.optmulti("t", "transact", "Execute a transact on startup. Transacts are executed before queries.", "TRANSACT");
|
opts.optopt("r", "read", "Read in file at path and execute transact for each edn", "READ");
|
||||||
|
opts.optopt("t", "transact", "Execute a transact on startup.", "TRANSACT");
|
||||||
opts.optflag("v", "version", "Print version and exit");
|
opts.optflag("v", "version", "Print version and exit");
|
||||||
|
|
||||||
let matches = match opts.parse(&args[1..]) {
|
let matches = match opts.parse(&args[1..]) {
|
||||||
|
@ -79,6 +80,10 @@ pub fn run() -> i32 {
|
||||||
last_arg = None;
|
last_arg = None;
|
||||||
Some(command_parser::Command::Transact(arg.clone()))
|
Some(command_parser::Command::Transact(arg.clone()))
|
||||||
},
|
},
|
||||||
|
Some("-r") => {
|
||||||
|
last_arg = None;
|
||||||
|
Some(command_parser::Command::Read(vec![arg.clone()]))
|
||||||
|
},
|
||||||
Some(_) |
|
Some(_) |
|
||||||
None => {
|
None => {
|
||||||
last_arg = Some(&arg);
|
last_arg = Some(&arg);
|
||||||
|
|
|
@ -21,6 +21,7 @@ use command_parser::{
|
||||||
SHORT_QUERY_COMMAND,
|
SHORT_QUERY_COMMAND,
|
||||||
LONG_TRANSACT_COMMAND,
|
LONG_TRANSACT_COMMAND,
|
||||||
SHORT_TRANSACT_COMMAND,
|
SHORT_TRANSACT_COMMAND,
|
||||||
|
READ_COMMAND,
|
||||||
};
|
};
|
||||||
use input::InputReader;
|
use input::InputReader;
|
||||||
use input::InputResult::{
|
use input::InputResult::{
|
||||||
|
@ -43,6 +44,7 @@ lazy_static! {
|
||||||
map.insert(SHORT_QUERY_COMMAND, "Shortcut for `.query`. Execute a query against the current open database.");
|
map.insert(SHORT_QUERY_COMMAND, "Shortcut for `.query`. Execute a query against the current open database.");
|
||||||
map.insert(LONG_TRANSACT_COMMAND, "Execute a transact against the current open database.");
|
map.insert(LONG_TRANSACT_COMMAND, "Execute a transact against the current open database.");
|
||||||
map.insert(SHORT_TRANSACT_COMMAND, "Shortcut for `.transact`. Execute a transact against the current open database.");
|
map.insert(SHORT_TRANSACT_COMMAND, "Shortcut for `.transact`. Execute a transact against the current open database.");
|
||||||
|
map.insert(READ_COMMAND, "Read in the file provided in argument. Transact each edn in turn.");
|
||||||
map
|
map
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -112,6 +114,7 @@ impl Repl {
|
||||||
},
|
},
|
||||||
Command::Query(query) => self.execute_query(query),
|
Command::Query(query) => self.execute_query(query),
|
||||||
Command::Transact(transaction) => self.execute_transact(transaction),
|
Command::Transact(transaction) => self.execute_transact(transaction),
|
||||||
|
Command::Read(file) => self.read_file(file),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +138,7 @@ impl Repl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_query(&self, query: String) {
|
fn execute_query(&self, query: String) {
|
||||||
let results = match self.store.query(query){
|
let results = match self.store.query(query){
|
||||||
Result::Ok(vals) => {
|
Result::Ok(vals) => {
|
||||||
vals
|
vals
|
||||||
|
@ -175,7 +178,7 @@ impl Repl {
|
||||||
println!("\n{}", output);
|
println!("\n{}", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_transact(&mut self, transaction: String) {
|
fn execute_transact(&mut self, transaction: String) {
|
||||||
match self.store.transact(transaction) {
|
match self.store.transact(transaction) {
|
||||||
Result::Ok(report) => println!("{:?}", report),
|
Result::Ok(report) => println!("{:?}", report),
|
||||||
Result::Err(err) => println!("{:?}.", err),
|
Result::Err(err) => println!("{:?}.", err),
|
||||||
|
@ -194,6 +197,12 @@ impl Repl {
|
||||||
TypedValue::Uuid(u) => format!("{}", u),
|
TypedValue::Uuid(u) => format!("{}", u),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_file(&self, files: Vec<String>) {
|
||||||
|
for file in files {
|
||||||
|
println!("Executing edn in file {}", file);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in a new issue