Read from file and execute
This commit is contained in:
parent
66c402b205
commit
de616a4f62
5 changed files with 89 additions and 22 deletions
|
@ -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"
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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,11 +207,52 @@ 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)]
|
||||||
|
|
Loading…
Reference in a new issue