Add import command. (#456) r=emily
This commit is contained in:
parent
c53da08a00
commit
39cf26aa76
3 changed files with 52 additions and 6 deletions
|
@ -45,6 +45,8 @@ pub static SCHEMA_COMMAND: &'static str = &"schema";
|
||||||
pub static LONG_TIMER_COMMAND: &'static str = &"timer";
|
pub static LONG_TIMER_COMMAND: &'static str = &"timer";
|
||||||
pub static LONG_TRANSACT_COMMAND: &'static str = &"transact";
|
pub static LONG_TRANSACT_COMMAND: &'static str = &"transact";
|
||||||
pub static SHORT_TRANSACT_COMMAND: &'static str = &"t";
|
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 LONG_EXIT_COMMAND: &'static str = &"exit";
|
||||||
pub static SHORT_EXIT_COMMAND: &'static str = &"e";
|
pub static SHORT_EXIT_COMMAND: &'static str = &"e";
|
||||||
pub static LONG_QUERY_EXPLAIN_COMMAND: &'static str = &"explain_query";
|
pub static LONG_QUERY_EXPLAIN_COMMAND: &'static str = &"explain_query";
|
||||||
|
@ -64,6 +66,7 @@ pub enum Command {
|
||||||
Sync(Vec<String>),
|
Sync(Vec<String>),
|
||||||
Timer(bool),
|
Timer(bool),
|
||||||
Transact(String),
|
Transact(String),
|
||||||
|
Import(String),
|
||||||
QueryExplain(String),
|
QueryExplain(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +88,7 @@ impl Command {
|
||||||
&Command::OpenEmpty(_) |
|
&Command::OpenEmpty(_) |
|
||||||
&Command::Close |
|
&Command::Close |
|
||||||
&Command::Exit |
|
&Command::Exit |
|
||||||
|
&Command::Import(_) |
|
||||||
&Command::Sync(_) |
|
&Command::Sync(_) |
|
||||||
&Command::Cache(_, _) |
|
&Command::Cache(_, _) |
|
||||||
&Command::Schema => true
|
&Command::Schema => true
|
||||||
|
@ -94,6 +98,7 @@ impl Command {
|
||||||
pub fn is_timed(&self) -> bool {
|
pub fn is_timed(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
&Command::Query(_) |
|
&Command::Query(_) |
|
||||||
|
&Command::Import(_) |
|
||||||
&Command::Transact(_) => true,
|
&Command::Transact(_) => true,
|
||||||
&Command::QueryExplain(_) |
|
&Command::QueryExplain(_) |
|
||||||
&Command::Timer(_) |
|
&Command::Timer(_) |
|
||||||
|
@ -113,6 +118,9 @@ impl Command {
|
||||||
&Command::Query(ref args) => {
|
&Command::Query(ref args) => {
|
||||||
format!(".{} {}", LONG_QUERY_COMMAND, args)
|
format!(".{} {}", LONG_QUERY_COMMAND, args)
|
||||||
},
|
},
|
||||||
|
&Command::Import(ref args) => {
|
||||||
|
format!(".{} {}", LONG_IMPORT_COMMAND, args)
|
||||||
|
},
|
||||||
&Command::Transact(ref args) => {
|
&Command::Transact(ref args) => {
|
||||||
format!(".{} {}", LONG_TRANSACT_COMMAND, args)
|
format!(".{} {}", LONG_TRANSACT_COMMAND, args)
|
||||||
},
|
},
|
||||||
|
@ -151,6 +159,7 @@ impl Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn command(s: &str) -> Result<Command, cli::Error> {
|
pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
|
let path = || many1::<String, _>(satisfy(|c: char| !c.is_whitespace()));
|
||||||
let argument = || many1::<String, _>(satisfy(|c: char| !c.is_whitespace()));
|
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 arguments = || sep_end_by::<Vec<_>, _, _>(many1(satisfy(|c: char| !c.is_whitespace())), many1::<Vec<_>, _>(space())).expected("arguments");
|
||||||
|
|
||||||
|
@ -264,6 +273,13 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
Ok(Command::Query(x))
|
Ok(Command::Query(x))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let import_parser = try(string(LONG_IMPORT_COMMAND)).or(try(string(SHORT_IMPORT_COMMAND)))
|
||||||
|
.with(spaces())
|
||||||
|
.with(path())
|
||||||
|
.map(|x| {
|
||||||
|
Ok(Command::Import(x))
|
||||||
|
});
|
||||||
|
|
||||||
let transact_parser = try(string(LONG_TRANSACT_COMMAND)).or(try(string(SHORT_TRANSACT_COMMAND)))
|
let transact_parser = try(string(LONG_TRANSACT_COMMAND)).or(try(string(SHORT_TRANSACT_COMMAND)))
|
||||||
.with(edn_arg_parser())
|
.with(edn_arg_parser())
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
|
@ -278,8 +294,9 @@ pub fn command(s: &str) -> Result<Command, cli::Error> {
|
||||||
});
|
});
|
||||||
spaces()
|
spaces()
|
||||||
.skip(token('.'))
|
.skip(token('.'))
|
||||||
.with(choice::<[&mut Parser<Input = _, Output = Result<Command, cli::Error>>; 12], _>
|
.with(choice::<[&mut Parser<Input = _, Output = Result<Command, cli::Error>>; 13], _>
|
||||||
([&mut try(help_parser),
|
([&mut try(help_parser),
|
||||||
|
&mut try(import_parser),
|
||||||
&mut try(timer_parser),
|
&mut try(timer_parser),
|
||||||
&mut try(cache_parser),
|
&mut try(cache_parser),
|
||||||
&mut try(open_parser),
|
&mut try(open_parser),
|
||||||
|
@ -564,6 +581,16 @@ mod tests {
|
||||||
assert_eq!(err.to_string(), format!("Invalid command {:?}", input));
|
assert_eq!(err.to_string(), format!("Invalid command {:?}", input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_import_parser() {
|
||||||
|
let input = ".import /foo/bar/";
|
||||||
|
let cmd = command(&input).expect("Expected import command");
|
||||||
|
match cmd {
|
||||||
|
Command::Import(path) => assert_eq!(path, "/foo/bar/"),
|
||||||
|
_ => panic!("Wrong command!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transact_parser_complete_edn() {
|
fn test_transact_parser_complete_edn() {
|
||||||
let input = ".t [[:db/add \"s\" :db/ident :foo/uuid] [:db/add \"r\" :db/ident :bar/uuid]]";
|
let input = ".t [[:db/add \"s\" :db/ident :foo/uuid] [:db/add \"r\" :db/ident :bar/uuid]]";
|
||||||
|
|
|
@ -53,6 +53,7 @@ pub fn run() -> i32 {
|
||||||
opts.optflag("h", "help", "Print this help message and exit");
|
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("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.optmulti("t", "transact", "Execute a transact on startup. Transacts are executed before queries.", "TRANSACT");
|
||||||
|
opts.optmulti("i", "import", "Execute an import on startup. Imports are executed before queries.", "PATH");
|
||||||
opts.optflag("v", "version", "Print version and exit");
|
opts.optflag("v", "version", "Print version and exit");
|
||||||
|
|
||||||
let matches = match opts.parse(&args[1..]) {
|
let matches = match opts.parse(&args[1..]) {
|
||||||
|
@ -84,6 +85,10 @@ pub fn run() -> i32 {
|
||||||
last_arg = None;
|
last_arg = None;
|
||||||
Some(command_parser::Command::Query(arg.clone()))
|
Some(command_parser::Command::Query(arg.clone()))
|
||||||
},
|
},
|
||||||
|
Some("-i") => {
|
||||||
|
last_arg = None;
|
||||||
|
Some(command_parser::Command::Import(arg.clone()))
|
||||||
|
},
|
||||||
Some("-t") => {
|
Some("-t") => {
|
||||||
last_arg = None;
|
last_arg = None;
|
||||||
Some(command_parser::Command::Transact(arg.clone()))
|
Some(command_parser::Command::Transact(arg.clone()))
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
// 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::BTreeMap;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ use command_parser::{
|
||||||
SHORT_QUERY_COMMAND,
|
SHORT_QUERY_COMMAND,
|
||||||
SCHEMA_COMMAND,
|
SCHEMA_COMMAND,
|
||||||
SYNC_COMMAND,
|
SYNC_COMMAND,
|
||||||
|
LONG_IMPORT_COMMAND,
|
||||||
LONG_TRANSACT_COMMAND,
|
LONG_TRANSACT_COMMAND,
|
||||||
SHORT_TRANSACT_COMMAND,
|
SHORT_TRANSACT_COMMAND,
|
||||||
LONG_EXIT_COMMAND,
|
LONG_EXIT_COMMAND,
|
||||||
|
@ -63,8 +64,8 @@ use input::InputResult::{
|
||||||
};
|
};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref COMMAND_HELP: HashMap<&'static str, &'static str> = {
|
static ref COMMAND_HELP: BTreeMap<&'static str, &'static str> = {
|
||||||
let mut map = HashMap::new();
|
let mut map = BTreeMap::new();
|
||||||
map.insert(LONG_EXIT_COMMAND, "Close the current database and exit the REPL.");
|
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(SHORT_EXIT_COMMAND, "Shortcut for `.exit`. Close the current database and exit the REPL.");
|
||||||
map.insert(HELP_COMMAND, "Show help for commands.");
|
map.insert(HELP_COMMAND, "Show help for commands.");
|
||||||
|
@ -76,6 +77,7 @@ lazy_static! {
|
||||||
map.insert(SYNC_COMMAND, "Synchronize database against a Sync Server URL for a provided user UUID.");
|
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(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(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(LONG_QUERY_EXPLAIN_COMMAND, "Show the SQL and query plan that would be executed for a given query.");
|
||||||
map.insert(SHORT_QUERY_EXPLAIN_COMMAND,
|
map.insert(SHORT_QUERY_EXPLAIN_COMMAND,
|
||||||
"Shortcut for `.explain_query`. Show the SQL and query plan that would be executed for a given query.");
|
"Shortcut for `.explain_query`. Show the SQL and query plan that would be executed for a given query.");
|
||||||
|
@ -210,6 +212,7 @@ impl Repl {
|
||||||
Err(e) => eprintln!("{}", e.to_string()),
|
Err(e) => eprintln!("{}", e.to_string()),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
Command::Import(path) => self.execute_import(path),
|
||||||
Command::Sync(args) => {
|
Command::Sync(args) => {
|
||||||
match self.store.sync(&args[0], &args[1]) {
|
match self.store.sync(&args[0], &args[1]) {
|
||||||
Ok(_) => println!("Synced!"),
|
Ok(_) => println!("Synced!"),
|
||||||
|
@ -249,6 +252,17 @@ impl Repl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execute_import<T>(&mut self, path: T)
|
||||||
|
where T: Into<String> {
|
||||||
|
use ::std::io::Read;
|
||||||
|
let path = path.into();
|
||||||
|
let mut content: String = "".to_string();
|
||||||
|
match ::std::fs::File::open(path.clone()).and_then(|mut f| f.read_to_string(&mut content)) {
|
||||||
|
Ok(_) => self.execute_transact(content),
|
||||||
|
Err(e) => eprintln!("Error reading file {}: {}", path, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn open<T>(&mut self, path: T) -> ::mentat::errors::Result<()>
|
fn open<T>(&mut self, path: T) -> ::mentat::errors::Result<()>
|
||||||
where T: Into<String> {
|
where T: Into<String> {
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
|
|
Loading…
Reference in a new issue