Skip to content
This repository was archived by the owner on Sep 12, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions tools/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
20 changes: 20 additions & 0 deletions tools/cli/src/mentat_cli/command_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,26 @@ impl Command {
&Command::Close => true
}
}

pub fn output(&self) -> String {
match self {
&Command::Query(ref args) => {
format!(".{} {}", LONG_QUERY_COMMAND, args)
},
&Command::Transact(ref args) => {
format!(".{} {}", LONG_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<Command, cli::Error> {
Expand Down
3 changes: 3 additions & 0 deletions tools/cli/src/mentat_cli/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
},
Expand Down
35 changes: 31 additions & 4 deletions tools/cli/src/mentat_cli/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -39,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.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..]) {
Expand All @@ -59,11 +64,33 @@ pub fn run() -> i32 {
return 0;
}

let db_name = matches.opt_str("d");

let repl = repl::Repl::new(db_name);
let mut last_arg: Option<&str> = None;
let cmds:Vec<command_parser::Command> = 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();
repl.unwrap().run(Some(cmds));

} else {
println!("{}", repl.err().unwrap());
}
Expand Down
91 changes: 85 additions & 6 deletions tools/cli/src/mentat_cli/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@

use std::collections::HashMap;

use mentat::query::QueryResults;
use mentat_core::TypedValue;

use command_parser::{
Command,
HELP_COMMAND,
OPEN_COMMAND
OPEN_COMMAND,
LONG_QUERY_COMMAND,
SHORT_QUERY_COMMAND,
LONG_TRANSACT_COMMAND,
SHORT_TRANSACT_COMMAND,
};
use input::InputReader;
use input::InputResult::{
Expand All @@ -32,28 +39,39 @@ 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(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
};
}

/// 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<String>) -> Result<Repl, String> {
let store = try!(Store::new(db_name.clone()).map_err(|e| e.to_string()));
pub fn new() -> Result<Repl, String> {
let store = Store::new(None).map_err(|e| e.to_string())?;
Ok(Repl{
store: store,
})
}

/// Runs the REPL interactively.
pub fn run(&mut self) {
pub fn run(&mut self, startup_commands: Option<Vec<Command>>) {
let mut input = InputReader::new();

if let Some(cmds) = startup_commands {
for command in cmds.iter() {
println!("{}", command.output());
self.handle_command(command.clone());
}
}

loop {
let res = input.read_input();

Expand Down Expand Up @@ -92,7 +110,8 @@ impl Repl {
Err(e) => println!("{}", e.to_string())
};
},
_ => unimplemented!(),
Command::Query(query) => self.execute_query(query),
Command::Transact(transaction) => self.execute_transact(transaction),
}
}

Expand All @@ -115,6 +134,66 @@ impl Repl {
}
}
}

pub fn execute_query(&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);
}

pub fn execute_transact(&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)]
Expand Down
14 changes: 12 additions & 2 deletions tools/cli/src/mentat_cli/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -48,4 +51,11 @@ impl Store {
self.open(None)
}

pub fn query(&self, query: String) -> Result<QueryResults, cli::Error> {
Ok(try!(self.conn.q_once(&self.handle, &query, None)))
}

pub fn transact(&mut self, transaction: String) -> Result<TxReport, cli::Error> {
Ok(try!(self.conn.transact(&mut self.handle, &transaction)))
}
}