From 525f8766b4eba055102d7bfb2e604b511bcfb46b Mon Sep 17 00:00:00 2001 From: Emily Toop Date: Fri, 26 May 2017 14:44:53 +0100 Subject: [PATCH 1/6] Address review comments =nalexander. * Bump rust version number. * Use `bail` when throwing errors. * Improve edn parser. * Remove references to unused `more` flag. * Improve naming of query and transact commands. --- tools/cli/src/mentat_cli/command_parser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cli/src/mentat_cli/command_parser.rs b/tools/cli/src/mentat_cli/command_parser.rs index 58a1d4bf1..3b9259775 100644 --- a/tools/cli/src/mentat_cli/command_parser.rs +++ b/tools/cli/src/mentat_cli/command_parser.rs @@ -115,13 +115,13 @@ pub fn command(s: &str) -> Result { let query_parser = try(string(LONG_QUERY_COMMAND)).or(try(string(SHORT_QUERY_COMMAND))) .with(edn_arg_parser()) .map(|x| { - Ok(Command::Query(x)) + Ok(Command::Query(x.into_iter().collect())) }); let transact_parser = try(string(LONG_TRANSACT_COMMAND)).or(try(string(SHORT_TRANSACT_COMMAND))) .with(edn_arg_parser()) .map( |x| { - Ok(Command::Transact(x)) + Ok(Command::Transact(x.into_iter().collect())) }); spaces() From 99e1c35377b6605a7fd5b1331936140aab520228 Mon Sep 17 00:00:00 2001 From: Emily Toop Date: Thu, 18 May 2017 15:16:12 +0100 Subject: [PATCH 2/6] Send queries and transactions to mentat and output the results move outputting query and transaction results out of store and into repl --- db/src/lib.rs | 2 +- tools/cli/Cargo.toml | 9 +++++ tools/cli/src/mentat_cli/input.rs | 3 ++ tools/cli/src/mentat_cli/lib.rs | 3 ++ tools/cli/src/mentat_cli/repl.rs | 65 +++++++++++++++++++++++++++++++ tools/cli/src/mentat_cli/store.rs | 14 ++++++- 6 files changed, 93 insertions(+), 3 deletions(-) diff --git a/db/src/lib.rs b/db/src/lib.rs index 07f5bc5bb..a67c6562b 100644 --- a/db/src/lib.rs +++ b/db/src/lib.rs @@ -43,7 +43,7 @@ mod entids; pub mod errors; mod metadata; mod schema; -mod types; +pub mod types; mod internal_types; mod upsert_resolution; mod tx; diff --git a/tools/cli/Cargo.toml b/tools/cli/Cargo.toml index 620a8ce4d..cd74523b5 100644 --- a/tools/cli/Cargo.toml +++ b/tools/cli/Cargo.toml @@ -34,3 +34,12 @@ path = "../../parser-utils" [dependencies.edn] path = "../../edn" + +[dependencies.mentat_query] +path = "../../query" + +[dependencies.mentat_core] +path = "../../core" + +[dependencies.mentat_db] +path = "../../db" diff --git a/tools/cli/src/mentat_cli/input.rs b/tools/cli/src/mentat_cli/input.rs index 6839dc611..2a614e5ad 100644 --- a/tools/cli/src/mentat_cli/input.rs +++ b/tools/cli/src/mentat_cli/input.rs @@ -109,6 +109,9 @@ impl InputReader { match cmd { Command::Query(_) | Command::Transact(_) if !cmd.is_complete() => { + // 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 + // which type of command we've found here. self.in_process_cmd = Some(cmd); Ok(More) }, diff --git a/tools/cli/src/mentat_cli/lib.rs b/tools/cli/src/mentat_cli/lib.rs index 8fd95231d..b2cf272d8 100644 --- a/tools/cli/src/mentat_cli/lib.rs +++ b/tools/cli/src/mentat_cli/lib.rs @@ -22,6 +22,9 @@ extern crate rusqlite; extern crate mentat; extern crate edn; +extern crate mentat_query; +extern crate mentat_core; +extern crate mentat_db; use getopts::Options; diff --git a/tools/cli/src/mentat_cli/repl.rs b/tools/cli/src/mentat_cli/repl.rs index 9507e7edf..a288f20eb 100644 --- a/tools/cli/src/mentat_cli/repl.rs +++ b/tools/cli/src/mentat_cli/repl.rs @@ -10,6 +10,9 @@ use std::collections::HashMap; +use mentat::query::QueryResults; +use mentat_core::TypedValue; + use command_parser::{ Command, HELP_COMMAND, @@ -92,6 +95,8 @@ impl Repl { Err(e) => println!("{}", e.to_string()) }; }, + Command::Query(query) => self.query_command(query), + Command::Transact(transaction) => self.transact_command(transaction), _ => unimplemented!(), } } @@ -115,6 +120,66 @@ impl Repl { } } } + + fn query_command(&self, query: String) { + let results = match self.store.query(query){ + Result::Ok(vals) => { + vals + }, + Result::Err(err) => return println!("{:?}.", err), + }; + + if results.is_empty() { + println!("No results found.") + } + + let mut output:String = String::new(); + match results { + QueryResults::Scalar(Some(val)) => { + output.push_str(&self.typed_value_as_string(val) ); + }, + QueryResults::Tuple(Some(vals)) => { + for val in vals { + output.push_str(&format!("{}\t", self.typed_value_as_string(val))); + } + }, + QueryResults::Coll(vv) => { + for val in vv { + output.push_str(&format!("{}\n", self.typed_value_as_string(val))); + } + }, + QueryResults::Rel(vvv) => { + for vv in vvv { + for v in vv { + output.push_str(&format!("{}\t", self.typed_value_as_string(v))); + } + output.push_str("\n"); + } + }, + _ => output.push_str(&format!("No results found.")) + } + println!("\n{}", output); + } + + fn transact_command(&mut self, transaction: String) { + match self.store.transact(transaction) { + Result::Ok(report) => println!("{:?}", report), + Result::Err(err) => println!("{:?}.", err), + } + } + + fn typed_value_as_string(&self, value: TypedValue) -> String { + match value { + TypedValue::Boolean(b) => if b { "true".to_string() } else { "false".to_string() }, + TypedValue::Double(d) => format!("{}", d), + TypedValue::Instant(i) => format!("{}", i), + TypedValue::Keyword(k) => format!("{}", k), + TypedValue::Long(l) => format!("{}", l), + TypedValue::Ref(r) => format!("{}", r), + TypedValue::String(s) => format!("{:?}", s.to_string()), + TypedValue::Uuid(u) => format!("{}", u), + } + } } #[cfg(test)] diff --git a/tools/cli/src/mentat_cli/store.rs b/tools/cli/src/mentat_cli/store.rs index a1689f63f..bc49443c6 100644 --- a/tools/cli/src/mentat_cli/store.rs +++ b/tools/cli/src/mentat_cli/store.rs @@ -9,13 +9,16 @@ // specific language governing permissions and limitations under the License. use rusqlite; + +use errors as cli; + use mentat::{ new_connection, }; +use mentat::query::QueryResults; use mentat::conn::Conn; - -use errors as cli; +use mentat_db::types::TxReport; pub struct Store { handle: rusqlite::Connection, @@ -48,4 +51,11 @@ impl Store { self.open(None) } + pub fn query(&self, query: String) -> Result { + Ok(try!(self.conn.q_once(&self.handle, &query, None))) + } + + pub fn transact(&mut self, transaction: String) -> Result { + Ok(try!(self.conn.transact(&mut self.handle, &transaction))) + } } From 825726ddf9896f1ecd2894709470fa484b9a4ca5 Mon Sep 17 00:00:00 2001 From: Emily Toop Date: Mon, 22 May 2017 17:11:00 +0100 Subject: [PATCH 3/6] Add query and transact commands to help --- tools/cli/src/mentat_cli/repl.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/cli/src/mentat_cli/repl.rs b/tools/cli/src/mentat_cli/repl.rs index a288f20eb..2eca3268f 100644 --- a/tools/cli/src/mentat_cli/repl.rs +++ b/tools/cli/src/mentat_cli/repl.rs @@ -16,7 +16,11 @@ use mentat_core::TypedValue; use command_parser::{ Command, HELP_COMMAND, - OPEN_COMMAND + OPEN_COMMAND, + QUERY_COMMAND, + ALT_QUERY_COMMAND, + TRANSACT_COMMAND, + ALT_TRANSACT_COMMAND }; use input::InputReader; use input::InputResult::{ @@ -35,6 +39,10 @@ lazy_static! { let mut map = HashMap::new(); map.insert(HELP_COMMAND, "Show help for commands."); map.insert(OPEN_COMMAND, "Open a database at path."); + map.insert(QUERY_COMMAND, "Execute a query against the current open database."); + map.insert(ALT_QUERY_COMMAND, "Shortcut for `.query`. Execute a query against the current open database."); + map.insert(TRANSACT_COMMAND, "Execute a transact against the current open database."); + map.insert(ALT_TRANSACT_COMMAND, "Shortcut for `.transact`. Execute a transact against the current open database."); map }; } From 002edeae1e9303e1f4ea35680c65faa3725d601d Mon Sep 17 00:00:00 2001 From: Emily Toop Date: Tue, 23 May 2017 10:34:07 +0100 Subject: [PATCH 4/6] Execute queries and transacts passed in at startup --- tools/cli/src/mentat_cli/lib.rs | 18 +++++++++++++++++- tools/cli/src/mentat_cli/repl.rs | 22 ++++++++++++++-------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/tools/cli/src/mentat_cli/lib.rs b/tools/cli/src/mentat_cli/lib.rs index b2cf272d8..e05339a79 100644 --- a/tools/cli/src/mentat_cli/lib.rs +++ b/tools/cli/src/mentat_cli/lib.rs @@ -42,6 +42,8 @@ pub fn run() -> i32 { opts.optopt("d", "", "The path to a database to open", "DATABASE"); opts.optflag("h", "help", "Print this help message and exit"); + opts.optopt("q", "query", "Execute a query on startup. Queries are executed after any transacts.", "QUERY"); + opts.optopt("t", "transact", "Execute a transact on startup. Transacts are executed before queries.", "TRANSACT"); opts.optflag("v", "version", "Print version and exit"); let matches = match opts.parse(&args[1..]) { @@ -64,9 +66,23 @@ pub fn run() -> i32 { let db_name = matches.opt_str("d"); + let transacts = matches.opt_strs("t"); + let queries = matches.opt_strs("q"); + + let mut cmds = Vec::with_capacity(transacts.len() + queries.len()); + + for transact in transacts { + cmds.push(command_parser::Command::Transact(transact)); + } + for query in queries { + cmds.push(command_parser::Command::Query(query)); + } + + let repl = repl::Repl::new(db_name); if repl.is_ok() { - repl.unwrap().run(); + repl.unwrap().run(Some(cmds)); + } else { println!("{}", repl.err().unwrap()); } diff --git a/tools/cli/src/mentat_cli/repl.rs b/tools/cli/src/mentat_cli/repl.rs index 2eca3268f..e493026bf 100644 --- a/tools/cli/src/mentat_cli/repl.rs +++ b/tools/cli/src/mentat_cli/repl.rs @@ -20,7 +20,7 @@ use command_parser::{ QUERY_COMMAND, ALT_QUERY_COMMAND, TRANSACT_COMMAND, - ALT_TRANSACT_COMMAND + ALT_TRANSACT_COMMAND, }; use input::InputReader; use input::InputResult::{ @@ -49,22 +49,29 @@ lazy_static! { /// Executes input and maintains state of persistent items. pub struct Repl { - store: Store, + store: Store } impl Repl { /// Constructs a new `Repl`. pub fn new(db_name: Option) -> Result { let store = try!(Store::new(db_name.clone()).map_err(|e| e.to_string())); + println!("Database {:?} opened", db_output_name(&db_name.unwrap_or("".to_string()))); Ok(Repl{ store: store, }) } /// Runs the REPL interactively. - pub fn run(&mut self) { + pub fn run(&mut self, startup_commands: Option>) { let mut input = InputReader::new(); + if let Some(cmds) = startup_commands { + for command in cmds.iter() { + self.handle_command(command.clone()); + } + } + loop { let res = input.read_input(); @@ -103,9 +110,8 @@ impl Repl { Err(e) => println!("{}", e.to_string()) }; }, - Command::Query(query) => self.query_command(query), - Command::Transact(transaction) => self.transact_command(transaction), - _ => unimplemented!(), + Command::Query(query) => self.execute_query(query), + Command::Transact(transaction) => self.execute_transact(transaction), } } @@ -129,7 +135,7 @@ impl Repl { } } - fn query_command(&self, query: String) { + pub fn execute_query(&self, query: String) { let results = match self.store.query(query){ Result::Ok(vals) => { vals @@ -169,7 +175,7 @@ impl Repl { println!("\n{}", output); } - fn transact_command(&mut self, transaction: String) { + pub fn execute_transact(&mut self, transaction: String) { match self.store.transact(transaction) { Result::Ok(report) => println!("{:?}", report), Result::Err(err) => println!("{:?}.", err), From c03b88eb4309f2cfd0bc162a734e88dca970ab86 Mon Sep 17 00:00:00 2001 From: Emily Toop Date: Fri, 26 May 2017 16:04:59 +0100 Subject: [PATCH 5/6] Execute command line args in order --- tools/cli/src/mentat_cli/command_parser.rs | 20 ++++++++++ tools/cli/src/mentat_cli/lib.rs | 44 +++++++++++++--------- tools/cli/src/mentat_cli/repl.rs | 6 +-- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/tools/cli/src/mentat_cli/command_parser.rs b/tools/cli/src/mentat_cli/command_parser.rs index 3b9259775..7f1eb0ae2 100644 --- a/tools/cli/src/mentat_cli/command_parser.rs +++ b/tools/cli/src/mentat_cli/command_parser.rs @@ -68,6 +68,26 @@ impl Command { &Command::Close => true } } + + pub fn output(&self) -> String { + match self { + &Command::Query(ref args) => { + format!(".{} {}", QUERY_COMMAND, args) + }, + &Command::Transact(ref args) => { + format!(".{} {}", TRANSACT_COMMAND, args) + }, + &Command::Help(ref args) => { + format!(".{} {:?}", HELP_COMMAND, args) + }, + &Command::Open(ref args) => { + format!(".{} {}", OPEN_COMMAND, args) + } + &Command::Close => { + format!(".{}", CLOSE_COMMAND) + }, + } + } } pub fn command(s: &str) -> Result { diff --git a/tools/cli/src/mentat_cli/lib.rs b/tools/cli/src/mentat_cli/lib.rs index e05339a79..bcc7c71c7 100644 --- a/tools/cli/src/mentat_cli/lib.rs +++ b/tools/cli/src/mentat_cli/lib.rs @@ -42,8 +42,8 @@ pub fn run() -> i32 { opts.optopt("d", "", "The path to a database to open", "DATABASE"); opts.optflag("h", "help", "Print this help message and exit"); - opts.optopt("q", "query", "Execute a query on startup. Queries are executed after any transacts.", "QUERY"); - opts.optopt("t", "transact", "Execute a transact on startup. Transacts are executed before queries.", "TRANSACT"); + opts.optmulti("q", "query", "Execute a query on startup. Queries are executed after any transacts.", "QUERY"); + opts.optmulti("t", "transact", "Execute a transact on startup. Transacts are executed before queries.", "TRANSACT"); opts.optflag("v", "version", "Print version and exit"); let matches = match opts.parse(&args[1..]) { @@ -64,22 +64,30 @@ pub fn run() -> i32 { return 0; } - let db_name = matches.opt_str("d"); - - let transacts = matches.opt_strs("t"); - let queries = matches.opt_strs("q"); - - let mut cmds = Vec::with_capacity(transacts.len() + queries.len()); - - for transact in transacts { - cmds.push(command_parser::Command::Transact(transact)); - } - for query in queries { - cmds.push(command_parser::Command::Query(query)); - } - - - let repl = repl::Repl::new(db_name); + let mut last_arg: Option<&str> = None; + let cmds:Vec = args.iter().filter_map(|arg| { + match last_arg { + Some("-d") => { + last_arg = None; + Some(command_parser::Command::Open(arg.clone())) + }, + Some("-q") => { + last_arg = None; + Some(command_parser::Command::Query(arg.clone())) + }, + Some("-t") => { + last_arg = None; + Some(command_parser::Command::Transact(arg.clone())) + }, + Some(_) | + None => { + last_arg = Some(&arg); + None + }, + } + }).collect(); + + let repl = repl::Repl::new(); if repl.is_ok() { repl.unwrap().run(Some(cmds)); diff --git a/tools/cli/src/mentat_cli/repl.rs b/tools/cli/src/mentat_cli/repl.rs index e493026bf..fd6b33765 100644 --- a/tools/cli/src/mentat_cli/repl.rs +++ b/tools/cli/src/mentat_cli/repl.rs @@ -54,9 +54,8 @@ pub struct Repl { impl Repl { /// Constructs a new `Repl`. - pub fn new(db_name: Option) -> Result { - let store = try!(Store::new(db_name.clone()).map_err(|e| e.to_string())); - println!("Database {:?} opened", db_output_name(&db_name.unwrap_or("".to_string()))); + pub fn new() -> Result { + let store = Store::new(None).map_err(|e| e.to_string())?; Ok(Repl{ store: store, }) @@ -68,6 +67,7 @@ impl Repl { if let Some(cmds) = startup_commands { for command in cmds.iter() { + println!("{}", command.output()); self.handle_command(command.clone()); } } From 36f6efa1d64ea1257b9ddf9e871925b101aefcae Mon Sep 17 00:00:00 2001 From: Emily Toop Date: Tue, 30 May 2017 11:03:47 +0100 Subject: [PATCH 6/6] Addressing rebase issues --- tools/cli/src/mentat_cli/command_parser.rs | 8 ++++---- tools/cli/src/mentat_cli/repl.rs | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/cli/src/mentat_cli/command_parser.rs b/tools/cli/src/mentat_cli/command_parser.rs index 7f1eb0ae2..aa81e4d71 100644 --- a/tools/cli/src/mentat_cli/command_parser.rs +++ b/tools/cli/src/mentat_cli/command_parser.rs @@ -72,10 +72,10 @@ impl Command { pub fn output(&self) -> String { match self { &Command::Query(ref args) => { - format!(".{} {}", QUERY_COMMAND, args) + format!(".{} {}", LONG_QUERY_COMMAND, args) }, &Command::Transact(ref args) => { - format!(".{} {}", TRANSACT_COMMAND, args) + format!(".{} {}", LONG_TRANSACT_COMMAND, args) }, &Command::Help(ref args) => { format!(".{} {:?}", HELP_COMMAND, args) @@ -135,13 +135,13 @@ pub fn command(s: &str) -> Result { let query_parser = try(string(LONG_QUERY_COMMAND)).or(try(string(SHORT_QUERY_COMMAND))) .with(edn_arg_parser()) .map(|x| { - Ok(Command::Query(x.into_iter().collect())) + Ok(Command::Query(x)) }); let transact_parser = try(string(LONG_TRANSACT_COMMAND)).or(try(string(SHORT_TRANSACT_COMMAND))) .with(edn_arg_parser()) .map( |x| { - Ok(Command::Transact(x.into_iter().collect())) + Ok(Command::Transact(x)) }); spaces() diff --git a/tools/cli/src/mentat_cli/repl.rs b/tools/cli/src/mentat_cli/repl.rs index fd6b33765..9f84060c3 100644 --- a/tools/cli/src/mentat_cli/repl.rs +++ b/tools/cli/src/mentat_cli/repl.rs @@ -17,10 +17,10 @@ use command_parser::{ Command, HELP_COMMAND, OPEN_COMMAND, - QUERY_COMMAND, - ALT_QUERY_COMMAND, - TRANSACT_COMMAND, - ALT_TRANSACT_COMMAND, + LONG_QUERY_COMMAND, + SHORT_QUERY_COMMAND, + LONG_TRANSACT_COMMAND, + SHORT_TRANSACT_COMMAND, }; use input::InputReader; use input::InputResult::{ @@ -39,10 +39,10 @@ lazy_static! { let mut map = HashMap::new(); map.insert(HELP_COMMAND, "Show help for commands."); map.insert(OPEN_COMMAND, "Open a database at path."); - map.insert(QUERY_COMMAND, "Execute a query against the current open database."); - map.insert(ALT_QUERY_COMMAND, "Shortcut for `.query`. Execute a query against the current open database."); - map.insert(TRANSACT_COMMAND, "Execute a transact against the current open database."); - map.insert(ALT_TRANSACT_COMMAND, "Shortcut for `.transact`. Execute a transact against the current open database."); + map.insert(LONG_QUERY_COMMAND, "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(SHORT_TRANSACT_COMMAND, "Shortcut for `.transact`. Execute a transact against the current open database."); map }; }