CLI improvements (#577) r=grisha

* Add a prepared query command to CLI.
* Print nanoseconds in the REPL. This is a good problem to have.
* Better CLI timing.
* Use release for 'cargo cli', debug for 'cargo debugcli'.
* Don't enable debug symbols in release builds.
* Clean up CLI code. Fixed order for help.
* Column-align help output.
This commit is contained in:
Richard Newman 2018-03-05 12:52:20 -08:00 committed by GitHub
parent d46535a7c2
commit 30bf827d16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 320 additions and 215 deletions

View file

@ -1,2 +1,3 @@
[alias]
cli = ["run", "-p", "mentat_cli"]
cli = ["run", "--release", "-p", "mentat_cli"]
debugcli = ["run", "-p", "mentat_cli"]

View file

@ -80,4 +80,4 @@ path = "tx-parser"
path = "tolstoy"
[profile.release]
debug = true
debug = false

View file

@ -9,6 +9,7 @@
// specific language governing permissions and limitations under the License.
use combine::{
Parser,
any,
eof,
look_ahead,
@ -16,58 +17,63 @@ use combine::{
satisfy,
sep_end_by,
token,
Parser
};
use combine::char::{
space,
spaces,
string
string,
};
use combine::combinator::{
choice,
try
try,
};
use errors as cli;
use edn;
use mentat::CacheDirection;
use mentat::{
CacheDirection,
};
pub static HELP_COMMAND: &'static str = &"help";
pub static OPEN_COMMAND: &'static str = &"open";
pub static OPEN_EMPTY_COMMAND: &'static str = &"empty";
pub static CACHE_COMMAND: &'static str = &"cache";
pub static CLOSE_COMMAND: &'static str = &"close";
pub static LONG_QUERY_COMMAND: &'static str = &"query";
pub static SHORT_QUERY_COMMAND: &'static str = &"q";
pub static SCHEMA_COMMAND: &'static str = &"schema";
pub static LONG_TIMER_COMMAND: &'static str = &"timer";
pub static LONG_TRANSACT_COMMAND: &'static str = &"transact";
pub static SHORT_TRANSACT_COMMAND: &'static str = &"t";
pub static LONG_IMPORT_COMMAND: &'static str = &"import";
pub static SHORT_IMPORT_COMMAND: &'static str = &"i";
pub static LONG_EXIT_COMMAND: &'static str = &"exit";
pub static SHORT_EXIT_COMMAND: &'static str = &"e";
pub static LONG_QUERY_EXPLAIN_COMMAND: &'static str = &"explain_query";
pub static SHORT_QUERY_EXPLAIN_COMMAND: &'static str = &"eq";
pub static SYNC_COMMAND: &'static str = &"sync";
pub static COMMAND_CACHE: &'static str = &"cache";
pub static COMMAND_CLOSE: &'static str = &"close";
pub static COMMAND_EXIT_LONG: &'static str = &"exit";
pub static COMMAND_EXIT_SHORT: &'static str = &"e";
pub static COMMAND_HELP: &'static str = &"help";
pub static COMMAND_IMPORT_LONG: &'static str = &"import";
pub static COMMAND_IMPORT_SHORT: &'static str = &"i";
pub static COMMAND_OPEN: &'static str = &"open";
pub static COMMAND_OPEN_EMPTY: &'static str = &"empty";
pub static COMMAND_QUERY_LONG: &'static str = &"query";
pub static COMMAND_QUERY_SHORT: &'static str = &"q";
pub static COMMAND_QUERY_EXPLAIN_LONG: &'static str = &"explain_query";
pub static COMMAND_QUERY_EXPLAIN_SHORT: &'static str = &"eq";
pub static COMMAND_QUERY_PREPARED_LONG: &'static str = &"query_prepared";
pub static COMMAND_SCHEMA: &'static str = &"schema";
pub static COMMAND_SYNC: &'static str = &"sync";
pub static COMMAND_TIMER_LONG: &'static str = &"timer";
pub static COMMAND_TRANSACT_LONG: &'static str = &"transact";
pub static COMMAND_TRANSACT_SHORT: &'static str = &"t";
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Command {
Cache(String, CacheDirection),
Close,
Exit,
Help(Vec<String>),
Import(String),
Open(String),
OpenEmpty(String),
Cache(String, CacheDirection),
Query(String),
QueryExplain(String),
QueryPrepared(String),
Schema,
Sync(Vec<String>),
Timer(bool),
Transact(String),
Import(String),
QueryExplain(String),
}
impl Command {
@ -78,81 +84,91 @@ impl Command {
pub fn is_complete(&self) -> bool {
match self {
&Command::Query(ref args) |
&Command::Transact(ref args) |
&Command::QueryExplain(ref args) => {
&Command::QueryExplain(ref args) |
&Command::QueryPrepared(ref args) |
&Command::Transact(ref args)
=> {
edn::parse::value(&args).is_ok()
},
&Command::Timer(_) |
&Command::Help(_) |
&Command::Open(_) |
&Command::OpenEmpty(_) |
&Command::Cache(_, _) |
&Command::Close |
&Command::Exit |
&Command::Help(_) |
&Command::Import(_) |
&Command::Sync(_) |
&Command::Cache(_, _) |
&Command::Schema => true
&Command::Open(_) |
&Command::OpenEmpty(_) |
&Command::Timer(_) |
&Command::Schema |
&Command::Sync(_)
=> true,
}
}
pub fn is_timed(&self) -> bool {
match self {
&Command::Query(_) |
&Command::Import(_) |
&Command::Transact(_) => true,
&Command::QueryExplain(_) |
&Command::Timer(_) |
&Command::Help(_) |
&Command::Open(_) |
&Command::OpenEmpty(_) |
&Command::Query(_) |
&Command::QueryPrepared(_) |
&Command::Transact(_)
=> true,
&Command::Cache(_, _) |
&Command::Close |
&Command::Exit |
&Command::Sync(_) |
&Command::Schema => false
&Command::Help(_) |
&Command::Open(_) |
&Command::OpenEmpty(_) |
&Command::QueryExplain(_) |
&Command::Timer(_) |
&Command::Schema |
&Command::Sync(_)
=> false,
}
}
pub fn output(&self) -> String {
match self {
&Command::Query(ref args) => {
format!(".{} {}", LONG_QUERY_COMMAND, args)
},
&Command::Import(ref args) => {
format!(".{} {}", LONG_IMPORT_COMMAND, args)
},
&Command::Transact(ref args) => {
format!(".{} {}", LONG_TRANSACT_COMMAND, args)
},
&Command::Cache(ref attr, ref direction) => {
format!(".{} {} {:?}", CACHE_COMMAND, attr, direction)
},
&Command::Timer(on) => {
format!(".{} {}", LONG_TIMER_COMMAND, on)
},
&Command::Help(ref args) => {
format!(".{} {:?}", HELP_COMMAND, args)
},
&Command::Open(ref args) => {
format!(".{} {}", OPEN_COMMAND, args)
},
&Command::OpenEmpty(ref args) => {
format!(".{} {}", OPEN_EMPTY_COMMAND, args)
format!(".{} {} {:?}", COMMAND_CACHE, attr, direction)
},
&Command::Close => {
format!(".{}", CLOSE_COMMAND)
format!(".{}", COMMAND_CLOSE)
},
&Command::Exit => {
format!(".{}", LONG_EXIT_COMMAND)
format!(".{}", COMMAND_EXIT_LONG)
},
&Command::Schema => {
format!(".{}", SCHEMA_COMMAND)
&Command::Help(ref args) => {
format!(".{} {:?}", COMMAND_HELP, args)
},
&Command::Sync(ref args) => {
format!(".{} {:?}", SYNC_COMMAND, args)
&Command::Import(ref args) => {
format!(".{} {}", COMMAND_IMPORT_LONG, args)
},
&Command::Open(ref args) => {
format!(".{} {}", COMMAND_OPEN, args)
},
&Command::OpenEmpty(ref args) => {
format!(".{} {}", COMMAND_OPEN_EMPTY, args)
},
&Command::Query(ref args) => {
format!(".{} {}", COMMAND_QUERY_LONG, args)
},
&Command::QueryExplain(ref args) => {
format!(".{} {}", LONG_QUERY_EXPLAIN_COMMAND, args)
format!(".{} {}", COMMAND_QUERY_EXPLAIN_LONG, args)
},
&Command::QueryPrepared(ref args) => {
format!(".{} {}", COMMAND_QUERY_PREPARED_LONG, args)
},
&Command::Schema => {
format!(".{}", COMMAND_SCHEMA)
},
&Command::Sync(ref args) => {
format!(".{} {:?}", COMMAND_SYNC, args)
},
&Command::Timer(on) => {
format!(".{} {}", COMMAND_TIMER_LONG, on)
},
&Command::Transact(ref args) => {
format!(".{} {}", COMMAND_TRANSACT_LONG, args)
},
}
}
@ -163,33 +179,74 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
let argument = || many1::<String, _>(satisfy(|c: char| !c.is_whitespace()));
let arguments = || sep_end_by::<Vec<_>, _, _>(many1(satisfy(|c: char| !c.is_whitespace())), many1::<Vec<_>, _>(space())).expected("arguments");
let help_parser = string(HELP_COMMAND)
.with(spaces())
.with(arguments())
.map(|args| {
Ok(Command::Help(args.clone()))
});
let timer_parser = string(LONG_TIMER_COMMAND)
.with(spaces())
.with(string("on").map(|_| true).or(string("off").map(|_| false)))
.map(|args| {
Ok(Command::Timer(args))
});
// Helpers.
let direction_parser = || string("forward")
.map(|_| CacheDirection::Forward)
.or(string("reverse").map(|_| CacheDirection::Reverse))
.or(string("both").map(|_| CacheDirection::Both));
let cache_parser = string(CACHE_COMMAND)
let edn_arg_parser = || spaces()
.with(look_ahead(string("[").or(string("{")))
.with(many1::<Vec<_>, _>(try(any())))
.and_then(|args| -> Result<String, cli::Error> {
Ok(args.iter().collect())
})
);
let no_arg_parser = || arguments()
.skip(spaces())
.skip(eof());
// Commands.
let cache_parser = string(COMMAND_CACHE)
.with(spaces())
.with(argument().skip(spaces()).and(direction_parser())
.map(|(arg, direction)| {
Ok(Command::Cache(arg, direction))
}));
let open_parser = string(OPEN_COMMAND)
let close_parser = string(COMMAND_CLOSE)
.with(no_arg_parser())
.map(|args| {
if !args.is_empty() {
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
}
Ok(Command::Close)
});
let exit_parser = try(string(COMMAND_EXIT_LONG)).or(try(string(COMMAND_EXIT_SHORT)))
.with(no_arg_parser())
.map(|args| {
if !args.is_empty() {
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
}
Ok(Command::Exit)
});
let explain_query_parser = try(string(COMMAND_QUERY_EXPLAIN_LONG))
.or(try(string(COMMAND_QUERY_EXPLAIN_SHORT)))
.with(edn_arg_parser())
.map(|x| {
Ok(Command::QueryExplain(x))
});
let help_parser = string(COMMAND_HELP)
.with(spaces())
.with(arguments())
.map(|args| {
Ok(Command::Help(args.clone()))
});
let import_parser = try(string(COMMAND_IMPORT_LONG)).or(try(string(COMMAND_IMPORT_SHORT)))
.with(spaces())
.with(path())
.map(|x| {
Ok(Command::Import(x))
});
let open_parser = string(COMMAND_OPEN)
.with(spaces())
.with(arguments())
.map(|args| {
@ -202,7 +259,7 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
Ok(Command::Open(args[0].clone()))
});
let open_empty_parser = string(OPEN_EMPTY_COMMAND)
let open_empty_parser = string(COMMAND_OPEN_EMPTY)
.with(spaces())
.with(arguments())
.map(|args| {
@ -215,20 +272,19 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
Ok(Command::OpenEmpty(args[0].clone()))
});
let no_arg_parser = || arguments()
.skip(spaces())
.skip(eof());
let query_parser = try(string(COMMAND_QUERY_LONG)).or(try(string(COMMAND_QUERY_SHORT)))
.with(edn_arg_parser())
.map(|x| {
Ok(Command::Query(x))
});
let close_parser = string(CLOSE_COMMAND)
.with(no_arg_parser())
.map(|args| {
if !args.is_empty() {
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
}
Ok(Command::Close)
});
let query_prepared_parser = string(COMMAND_QUERY_PREPARED_LONG)
.with(edn_arg_parser())
.map(|x| {
Ok(Command::QueryPrepared(x))
});
let schema_parser = string(SCHEMA_COMMAND)
let schema_parser = string(COMMAND_SCHEMA)
.with(no_arg_parser())
.map(|args| {
if !args.is_empty() {
@ -237,7 +293,7 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
Ok(Command::Schema)
});
let sync_parser = string(SYNC_COMMAND)
let sync_parser = string(COMMAND_SYNC)
.with(spaces())
.with(arguments())
.map(|args| {
@ -250,51 +306,22 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
Ok(Command::Sync(args.clone()))
});
let exit_parser = try(string(LONG_EXIT_COMMAND)).or(try(string(SHORT_EXIT_COMMAND)))
.with(no_arg_parser())
.map(|args| {
if !args.is_empty() {
bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[0])) );
}
Ok(Command::Exit)
});
let edn_arg_parser = || spaces()
.with(look_ahead(string("[").or(string("{")))
.with(many1::<Vec<_>, _>(try(any())))
.and_then(|args| -> Result<String, cli::Error> {
Ok(args.iter().collect())
})
);
let query_parser = try(string(LONG_QUERY_COMMAND)).or(try(string(SHORT_QUERY_COMMAND)))
.with(edn_arg_parser())
.map(|x| {
Ok(Command::Query(x))
});
let import_parser = try(string(LONG_IMPORT_COMMAND)).or(try(string(SHORT_IMPORT_COMMAND)))
let timer_parser = string(COMMAND_TIMER_LONG)
.with(spaces())
.with(path())
.map(|x| {
Ok(Command::Import(x))
.with(string("on").map(|_| true).or(string("off").map(|_| false)))
.map(|args| {
Ok(Command::Timer(args))
});
let transact_parser = try(string(LONG_TRANSACT_COMMAND)).or(try(string(SHORT_TRANSACT_COMMAND)))
let transact_parser = try(string(COMMAND_TRANSACT_LONG)).or(try(string(COMMAND_TRANSACT_SHORT)))
.with(edn_arg_parser())
.map(|x| {
Ok(Command::Transact(x))
});
let explain_query_parser = try(string(LONG_QUERY_EXPLAIN_COMMAND))
.or(try(string(SHORT_QUERY_EXPLAIN_COMMAND)))
.with(edn_arg_parser())
.map(|x| {
Ok(Command::QueryExplain(x))
});
spaces()
.skip(token('.'))
.with(choice::<[&mut Parser<Input = _, Output = Result<Command, cli::Error>>; 13], _>
.with(choice::<[&mut Parser<Input = _, Output = Result<Command, cli::Error>>; 14], _>
([&mut try(help_parser),
&mut try(import_parser),
&mut try(timer_parser),
@ -304,6 +331,7 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
&mut try(close_parser),
&mut try(explain_query_parser),
&mut try(exit_parser),
&mut try(query_prepared_parser),
&mut try(query_parser),
&mut try(schema_parser),
&mut try(sync_parser),

View file

@ -132,6 +132,9 @@ impl InputReader {
// Therefore, we add the newly read in line to the existing command args.
// If there is no in process command, we parse the read in line as a new command.
let cmd = match &self.in_process_cmd {
&Some(Command::QueryPrepared(ref args)) => {
Ok(Command::QueryPrepared(args.clone() + " " + &line))
},
&Some(Command::Query(ref args)) => {
Ok(Command::Query(args.clone() + " " + &line))
},
@ -147,6 +150,7 @@ impl InputReader {
Ok(cmd) => {
match cmd {
Command::Query(_) |
Command::QueryPrepared(_) |
Command::Transact(_) |
Command::QueryExplain(_) if !cmd.is_complete() => {
// A query or transact is complete if it contains a valid EDN.

View file

@ -8,7 +8,6 @@
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
use std::collections::BTreeMap;
use std::io::Write;
use std::process;
@ -39,49 +38,62 @@ use mentat::{
use command_parser::{
Command,
HELP_COMMAND,
OPEN_COMMAND,
CACHE_COMMAND,
LONG_QUERY_COMMAND,
SHORT_QUERY_COMMAND,
SCHEMA_COMMAND,
SYNC_COMMAND,
LONG_IMPORT_COMMAND,
LONG_TRANSACT_COMMAND,
SHORT_TRANSACT_COMMAND,
LONG_EXIT_COMMAND,
SHORT_EXIT_COMMAND,
LONG_QUERY_EXPLAIN_COMMAND,
SHORT_QUERY_EXPLAIN_COMMAND,
};
use command_parser::{
COMMAND_CACHE,
COMMAND_EXIT_LONG,
COMMAND_EXIT_SHORT,
COMMAND_HELP,
COMMAND_IMPORT_LONG,
COMMAND_OPEN,
COMMAND_QUERY_LONG,
COMMAND_QUERY_SHORT,
COMMAND_QUERY_EXPLAIN_LONG,
COMMAND_QUERY_EXPLAIN_SHORT,
COMMAND_QUERY_PREPARED_LONG,
COMMAND_SCHEMA,
COMMAND_SYNC,
COMMAND_TRANSACT_LONG,
COMMAND_TRANSACT_SHORT,
};
use input::InputReader;
use input::InputResult::{
MetaCommand,
Empty,
More,
Eof,
MetaCommand,
More,
};
lazy_static! {
static ref COMMAND_HELP: BTreeMap<&'static str, &'static str> = {
let mut map = BTreeMap::new();
map.insert(LONG_EXIT_COMMAND, "Close the current database and exit the REPL.");
map.insert(SHORT_EXIT_COMMAND, "Shortcut for `.exit`. Close the current database and exit the REPL.");
map.insert(HELP_COMMAND, "Show help for commands.");
map.insert(OPEN_COMMAND, "Open a database at path.");
map.insert(CACHE_COMMAND, "Cache an attribute. Usage: `.cache :foo/bar reverse`");
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(SCHEMA_COMMAND, "Output the schema for the current open database.");
map.insert(SYNC_COMMAND, "Synchronize database against a Sync Server URL for a provided user UUID.");
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(LONG_IMPORT_COMMAND, "Transact the contents of a file against the current open database.");
map.insert(LONG_QUERY_EXPLAIN_COMMAND, "Show the SQL and query plan that would be executed for a given query.");
map.insert(SHORT_QUERY_EXPLAIN_COMMAND,
"Shortcut for `.explain_query`. Show the SQL and query plan that would be executed for a given query.");
map
static ref HELP_COMMANDS: Vec<(&'static str, &'static str)> = {
vec![
(COMMAND_HELP, "Show this message."),
(COMMAND_EXIT_LONG, "Close the current database and exit the REPL."),
(COMMAND_EXIT_SHORT, "Shortcut for `.exit`. Close the current database and exit the REPL."),
(COMMAND_OPEN, "Open a database at path."),
(COMMAND_SCHEMA, "Output the schema for the current open database."),
(COMMAND_IMPORT_LONG, "Transact the contents of a file against the current open database."),
(COMMAND_QUERY_LONG, "Execute a query against the current open database."),
(COMMAND_QUERY_SHORT, "Shortcut for `.query`. Execute a query against the current open database."),
(COMMAND_QUERY_PREPARED_LONG, "Prepare a query against the current open database, then run it, timed."),
(COMMAND_TRANSACT_LONG, "Execute a transact against the current open database."),
(COMMAND_TRANSACT_SHORT, "Shortcut for `.transact`. Execute a transact against the current open database."),
(COMMAND_QUERY_EXPLAIN_LONG, "Show the SQL and query plan that would be executed for a given query."),
(COMMAND_QUERY_EXPLAIN_SHORT, "Shortcut for `.explain_query`. Show the SQL and query plan that would be executed for a given query."),
(COMMAND_CACHE, "Cache an attribute. Usage: `.cache :foo/bar reverse`"),
(COMMAND_SYNC, "Synchronize the database against a Sync Server URL for a provided user UUID."),
]
};
}
@ -101,12 +113,25 @@ fn parse_namespaced_keyword(input: &str) -> Option<NamespacedKeyword> {
}
fn format_time(duration: Duration) {
let m_nanos = duration.num_nanoseconds();
if let Some(nanos) = m_nanos {
if nanos < 1_000 {
eprintln!("{bold}{nanos}{reset}ns",
bold = style::Bold,
nanos = nanos,
reset = style::Reset);
return;
}
}
let m_micros = duration.num_microseconds();
if let Some(micros) = m_micros {
if micros < 10_000 {
eprintln!("{bold}{micros}{reset}µs",
let ns = m_nanos.unwrap_or(0) / 1000;
eprintln!("{bold}{micros}.{ns}{reset}µs",
bold = style::Bold,
micros = micros,
ns = ns,
reset = style::Reset);
return;
}
@ -146,7 +171,7 @@ impl Repl {
/// Constructs a new `Repl`.
pub fn new() -> Result<Repl, String> {
let store = Store::open("").map_err(|e| e.to_string())?;
Ok(Repl{
Ok(Repl {
path: "".to_string(),
store: store,
timer_on: false,
@ -176,7 +201,7 @@ impl Repl {
Ok(More) => (),
Ok(Eof) => {
if input.is_tty() {
println!("");
println!();
}
break;
},
@ -198,54 +223,102 @@ impl Repl {
/// Runs a single command input.
fn handle_command(&mut self, cmd: Command) {
let should_time = self.timer_on && cmd.is_timed();
let should_print_times = self.timer_on && cmd.is_timed();
let start = PreciseTime::now();
let mut start = PreciseTime::now();
let mut end: Option<PreciseTime> = None;
match cmd {
Command::Help(args) => self.help_command(args),
Command::Timer(on) => self.toggle_timer(on),
Command::Cache(attr, direction) => self.cache(attr, direction),
Command::Cache(attr, direction) => {
self.cache(attr, direction);
},
Command::Close => {
self.close();
},
Command::Exit => {
self.close();
eprintln!("Exiting…");
process::exit(0);
},
Command::Help(args) => {
self.help_command(args);
},
Command::Import(path) => {
self.execute_import(path);
},
Command::Open(db) => {
match self.open(db) {
Ok(_) => println!("Database {:?} opened", self.db_name()),
Err(e) => eprintln!("{}", e.to_string()),
};
},
Command::Import(path) => self.execute_import(path),
Command::Sync(args) => {
match self.store.sync(&args[0], &args[1]) {
Ok(_) => println!("Synced!"),
Err(e) => eprintln!("{:?}", e)
};
}
Command::OpenEmpty(db) => {
match self.open_empty(db) {
Ok(_) => println!("Empty database {:?} opened", self.db_name()),
Err(e) => eprintln!("{}", e.to_string()),
};
},
Command::Close => self.close(),
Command::Query(query) => self.execute_query(query),
Command::QueryExplain(query) => self.explain_query(query),
Command::Query(query) => {
self.store
.q_once(query.as_str(), None)
.map_err(|e| e.into())
.and_then(|o| {
end = Some(PreciseTime::now());
self.print_results(o)
})
.map_err(|err| {
eprintln!("{:?}.", err);
})
.ok();
},
Command::QueryExplain(query) => {
self.explain_query(query);
},
Command::QueryPrepared(query) => {
self.store
.q_prepare(query.as_str(), None)
.and_then(|mut p| {
let prepare_end = PreciseTime::now();
if should_print_times {
eprint_out("Prepare time");
eprint!(": ");
format_time(start.to(prepare_end));
}
// This is a hack.
start = PreciseTime::now();
let r = p.run(None);
end = Some(PreciseTime::now());
return r;
})
.map(|o| self.print_results(o))
.map_err(|err| {
eprintln!("{:?}.", err);
})
.ok();
},
Command::Schema => {
let edn = self.store.conn().current_schema().to_edn_value();
match edn.to_pretty(120) {
Ok(s) => println!("{}", s),
Err(e) => eprintln!("{}", e)
};
}
Command::Transact(transaction) => self.execute_transact(transaction),
Command::Exit => {
self.close();
eprintln!("Exiting...");
process::exit(0);
},
Command::Sync(args) => {
match self.store.sync(&args[0], &args[1]) {
Ok(_) => println!("Synced!"),
Err(e) => eprintln!("{:?}", e)
};
}
Command::Timer(on) => {
self.toggle_timer(on);
},
Command::Transact(transaction) => {
self.execute_transact(transaction);
},
}
let end = PreciseTime::now();
if should_time {
let end = end.unwrap_or_else(PreciseTime::now);
if should_print_times {
eprint_out("Run time");
eprint!(": ");
format_time(start.to(end));
@ -301,32 +374,31 @@ impl Repl {
}
fn help_command(&self, args: Vec<String>) {
let stdout = ::std::io::stdout();
let mut output = TabWriter::new(stdout.lock());
if args.is_empty() {
for (cmd, msg) in COMMAND_HELP.iter() {
println!(".{} - {}", cmd, msg);
for &(cmd, msg) in HELP_COMMANDS.iter() {
write!(output, ".{}\t", cmd).unwrap();
writeln!(output, "{}", msg).unwrap();
}
} else {
for mut arg in args {
if arg.chars().nth(0).unwrap() == '.' {
arg.remove(0);
}
let msg = COMMAND_HELP.get(arg.as_str());
if msg.is_some() {
println!(".{} - {}", arg, msg.unwrap());
if let Some(&(cmd, msg)) = HELP_COMMANDS.iter()
.filter(|&&(c, _)| c == arg.as_str())
.next() {
write!(output, ".{}\t", cmd).unwrap();
writeln!(output, "{}", msg).unwrap();
} else {
eprintln!("Unrecognised command {}", arg);
return;
}
}
}
}
pub fn execute_query(&self, query: String) {
self.store.q_once(query.as_str(), None)
.map_err(|e| e.into())
.and_then(|o| self.print_results(o))
.map_err(|err| {
eprintln!("{:?}.", err);
}).ok();
writeln!(output, "").unwrap();
output.flush().unwrap();
}
fn print_results(&self, query_output: QueryOutput) -> Result<(), ::errors::Error> {