Read from file and execute

This commit is contained in:
Emily Toop 2017-05-26 14:01:32 +01:00
parent 66c402b205
commit de616a4f62
5 changed files with 89 additions and 22 deletions

View file

@ -12,14 +12,14 @@ doc = false
test = false test = false
[dependencies] [dependencies]
combine = "2.2.2"
getopts = "0.2" getopts = "0.2"
env_logger = "0.3" env_logger = "0.3"
error-chain = "0.8.1"
lazy_static = "0.2.2"
linefeed = "0.1" linefeed = "0.1"
log = "0.3" log = "0.3"
tempfile = "1.1" tempfile = "1.1"
combine = "2.2.2"
lazy_static = "0.2.2"
error-chain = "0.8.1"
[dependencies.rusqlite] [dependencies.rusqlite]
version = "0.11" version = "0.11"

View file

@ -59,16 +59,17 @@ impl Command {
/// false is returned if the command is not considered valid. /// false is returned if the command is not considered valid.
/// Defaults to true for all commands except Query and Transact. /// Defaults to true for all commands except Query and Transact.
/// TODO: for query and transact commands, they will be considered complete if a parsable EDN has been entered as an argument /// TODO: for query and transact commands, they will be considered complete if a parsable EDN has been entered as an argument
pub fn is_complete(&self) -> bool { pub fn is_complete(&self) -> (bool, Option<edn::ParseError>) {
match self { match self {
&Command::Query(ref args) | &Command::Query(ref args) |
&Command::Transact(ref args) => { &Command::Transact(ref args) => {
edn::parse::value(&args).is_ok() let r = edn::parse::value(&args);
(r.is_ok(), r.err())
}, },
&Command::Help(_) | &Command::Help(_) |
&Command::Open(_) | &Command::Open(_) |
&Command::Close | &Command::Close |
&Command::Read(_) => true &Command::Read(_) => (true, None)
} }
} }
@ -155,7 +156,8 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
.with(arguments()) .with(arguments())
.map(|args| { .map(|args| {
// strip quotes from file paths. // strip quotes from file paths.
let mut sargs = Vec::with_capacity(args.len()); // not sure how to map this and still throw the error so doing it the old fashioned way
let mut files = Vec::with_capacity(args.len());
for arg in args.iter() { for arg in args.iter() {
let start_char = arg.chars().nth(0); let start_char = arg.chars().nth(0);
match start_char { match start_char {
@ -163,19 +165,20 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
Some('\'') => { Some('\'') => {
let separator = start_char.unwrap(); let separator = start_char.unwrap();
if arg.ends_with(separator) { if arg.ends_with(separator) {
sargs.push(arg.split(separator).collect::<Vec<&str>>().into_iter().collect()); files.push(arg.split(separator).collect::<Vec<&str>>().into_iter().collect());
} else { } else {
return Err(cli::ErrorKind::CommandParse(format!("Unrecognized argument {}", arg)).into()); return Err(cli::ErrorKind::CommandParse(format!("Unrecognized argument {}", arg)).into());
} }
}, },
_ => sargs.push(arg.clone()), _ => files.push(arg.clone()),
} }
} }
println!("args: {:?}", sargs);
// check that we have at least one argument
if args.len() == 0 { if args.len() == 0 {
return Err(cli::ErrorKind::CommandParse("Missing required argument".to_string()).into()); return Err(cli::ErrorKind::CommandParse("Missing required argument".to_string()).into());
} }
Ok(Command::Read(sargs.clone())) Ok(Command::Read(files.clone()))
}); });
spaces() spaces()

View file

@ -13,6 +13,7 @@
use rusqlite; use rusqlite;
use mentat::errors as mentat; use mentat::errors as mentat;
use edn;
error_chain! { error_chain! {
types { types {
@ -20,6 +21,7 @@ error_chain! {
} }
foreign_links { foreign_links {
EdnParseError(edn::ParseError);
Rusqlite(rusqlite::Error); Rusqlite(rusqlite::Error);
} }
@ -32,5 +34,10 @@ error_chain! {
description("An error occured parsing the entered command") description("An error occured parsing the entered command")
display("{}", message) display("{}", message)
} }
FileError(filename: String, message: String) {
description("An error occured while reading file")
display("Unable to open file {}: {}", filename, message)
}
} }
} }

View file

@ -102,13 +102,20 @@ impl InputReader {
Command::Transact(args.clone() + " " + &line) Command::Transact(args.clone() + " " + &line)
}, },
_ => { _ => {
try!(command(&self.buffer)) let res = command(&self.buffer);
match res {
Ok(cmd) => cmd,
Err(err) => {
self.buffer.clear();
bail!(err)
}
}
} }
}; };
match cmd { match cmd {
Command::Query(_) | Command::Query(_) |
Command::Transact(_) if !cmd.is_complete() => { Command::Transact(_) if !cmd.is_complete().0 => {
// a query or transact is complete if it contains a valid edn. // a query or transact is complete if it contains a valid edn.
// if the command is not complete, ask for more from the repl and remember // if the command is not complete, ask for more from the repl and remember
// which type of command we've found here. // which type of command we've found here.

View file

@ -9,6 +9,15 @@
// specific language governing permissions and limitations under the License. // specific language governing permissions and limitations under the License.
use std::collections::HashMap; use std::collections::HashMap;
use std::io::BufReader;
use std::io::BufRead;
use std::fs::File;
use std::path::Path;
use error_chain::ChainedError;
use errors as cli;
use edn;
use mentat::query::QueryResults; use mentat::query::QueryResults;
use mentat_core::TypedValue; use mentat_core::TypedValue;
@ -44,7 +53,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.insert(READ_COMMAND, "Read a file containing one or more complete edn transact statements. Transacts each edn in turn.");
map map
}; };
} }
@ -90,7 +99,7 @@ impl Repl {
} }
break; break;
}, },
Err(e) => println!("{}", e.to_string()), Err(e) => println!("{}", e.display()),
} }
} }
} }
@ -102,19 +111,19 @@ impl Repl {
Command::Open(db) => { Command::Open(db) => {
match self.store.open(Some(db.clone())) { match self.store.open(Some(db.clone())) {
Ok(_) => println!("Database {:?} opened", db_output_name(&db)), Ok(_) => println!("Database {:?} opened", db_output_name(&db)),
Err(e) => println!("{}", e.to_string()) Err(e) => println!("{}", e.display())
}; };
}, },
Command::Close => { Command::Close => {
let old_db_name = self.store.db_name.clone(); let old_db_name = self.store.db_name.clone();
match self.store.close() { match self.store.close() {
Ok(_) => println!("Database {:?} closed", db_output_name(&old_db_name)), Ok(_) => println!("Database {:?} closed", db_output_name(&old_db_name)),
Err(e) => println!("{}", e.to_string()) Err(e) => println!("{}", e.display())
}; };
}, },
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), Command::Read(file) => self.read_files(file),
} }
} }
@ -143,7 +152,7 @@ impl Repl {
Result::Ok(vals) => { Result::Ok(vals) => {
vals vals
}, },
Result::Err(err) => return println!("{:?}.", err), Result::Err(err) => return println!("{}.", err.display()),
}; };
if results.is_empty() { if results.is_empty() {
@ -181,7 +190,7 @@ impl Repl {
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.display()),
} }
} }
@ -198,12 +207,53 @@ impl Repl {
} }
} }
fn read_file(&self, files: Vec<String>) { fn read_files(&mut self, files: Vec<String>) {
for file in files { for file in files {
println!("Executing edn in file {}", file); let res = self.read_file(file);
if res.is_err() {
match res.unwrap_err() {
cli::Error(err, _) => { println!("{}", err) },
} }
} }
} }
}
fn read_file(&mut self, file: String) -> Result<(), cli::Error> {
let path = Path::new(&file);
let display = path.display();
let f = match File::open(&path) {
Err(err) => bail!(cli::ErrorKind::FileError(display.to_string(), err.to_string())),
Ok(file) => file,
};
let mut buffer = String::new();
let mut cmd_err: Option<edn::ParseError> = None;
let file = BufReader::new(&f);
for line in file.lines() {
let l = line.unwrap();
println!("{}", l);
buffer.push_str(&l);
let cmd = Command::Transact(buffer.to_string());
let (is_complete, err) = cmd.is_complete();
if is_complete {
self.handle_command(cmd);
buffer.clear();
cmd_err = None;
} else {
cmd_err = err;
}
}
if let Some(err) = cmd_err {
println!("\nUnable to parse edn: {}", err.to_string());
}
Ok(())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {