diff --git a/tools/cli/src/mentat_cli/command_parser.rs b/tools/cli/src/mentat_cli/command_parser.rs index 5d9f8568..131117bf 100644 --- a/tools/cli/src/mentat_cli/command_parser.rs +++ b/tools/cli/src/mentat_cli/command_parser.rs @@ -38,6 +38,7 @@ 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 ATTRIBUTES_COMMAND: &'static str = &"attributes"; pub static LONG_TRANSACT_COMMAND: &'static str = &"transact"; pub static SHORT_TRANSACT_COMMAND: &'static str = &"t"; pub static LONG_EXIT_COMMAND: &'static str = &"exit"; @@ -51,6 +52,7 @@ pub enum Command { Open(String), Query(String), Schema, + Attributes, Transact(String), } @@ -69,7 +71,8 @@ impl Command { &Command::Open(_) | &Command::Close | &Command::Exit | - &Command::Schema => true + &Command::Schema | + &Command::Attributes => true } } @@ -96,6 +99,9 @@ impl Command { &Command::Schema => { format!(".{}", SCHEMA_COMMAND) }, + &Command::Attributes => { + format!(".{}", ATTRIBUTES_COMMAND) + }, } } } @@ -144,6 +150,15 @@ pub fn command(s: &str) -> Result { } Ok(Command::Schema) }); + + let attributes_parser = string(ATTRIBUTES_COMMAND) + .with(no_arg_parser()) + .map(|args| { + if !args.is_empty() { + bail!(cli::ErrorKind::CommandParse(format!("Unrecognized argument {:?}", args[0])) ); + } + Ok(Command::Attributes) + }); let exit_parser = try(string(LONG_EXIT_COMMAND)).or(try(string(SHORT_EXIT_COMMAND))) .with(no_arg_parser()) @@ -176,13 +191,14 @@ pub fn command(s: &str) -> Result { spaces() .skip(token('.')) - .with(choice::<[&mut Parser>; 7], _> + .with(choice::<[&mut Parser>; 8], _> ([&mut try(help_parser), &mut try(open_parser), &mut try(close_parser), &mut try(exit_parser), &mut try(query_parser), &mut try(schema_parser), + &mut try(attributes_parser), &mut try(transact_parser)])) .parse(s) .unwrap_or((Err(cli::ErrorKind::CommandParse(format!("Invalid command {:?}", s)).into()), "")).0 @@ -390,6 +406,33 @@ mod tests { } } + #[test] + fn test_attributes_parser_with_args() { + let input = ".attributes arg1"; + let err = command(&input).expect_err("Expected an error"); + assert_eq!(err.to_string(), format!("Invalid command {:?}", input)); + } + + #[test] + fn test_attributes_parser_no_args() { + let input = ".attributes"; + let cmd = command(&input).expect("Expected attributes command"); + match cmd { + Command::Attributes => assert!(true), + _ => assert!(false) + } + } + + #[test] + fn test_attributes_parser_no_args_trailing_whitespace() { + let input = ".attributes "; + let cmd = command(&input).expect("Expected attributes command"); + match cmd { + Command::Attributes => assert!(true), + _ => assert!(false) + } + } + #[test] fn test_query_parser_complete_edn() { let input = ".q [:find ?x :where [?x foo/bar ?y]]"; diff --git a/tools/cli/src/mentat_cli/repl.rs b/tools/cli/src/mentat_cli/repl.rs index acf1d208..e4acc9e7 100644 --- a/tools/cli/src/mentat_cli/repl.rs +++ b/tools/cli/src/mentat_cli/repl.rs @@ -27,6 +27,7 @@ use command_parser::{ SHORT_TRANSACT_COMMAND, LONG_EXIT_COMMAND, SHORT_EXIT_COMMAND, + ATTRIBUTES_COMMAND, }; use input::InputReader; use input::InputResult::{ @@ -43,6 +44,7 @@ use store::{ lazy_static! { static ref COMMAND_HELP: HashMap<&'static str, &'static str> = { let mut map = HashMap::new(); + map.insert(ATTRIBUTES_COMMAND, "Output the attributes for the schema in the current open database."); 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."); @@ -120,8 +122,14 @@ impl Repl { Ok(s) => println!("{}", s), Err(e) => println!("{}", e) }; - - } + }, + Command::Attributes => { + let edn = self.store.fetch_attributes(); + match edn.to_pretty(120) { + Ok(s) => println!("{}", s), + Err(e) => println!("{}", e) + }; + }, Command::Transact(transaction) => self.execute_transact(transaction), Command::Exit => { self.close(); diff --git a/tools/cli/src/mentat_cli/store.rs b/tools/cli/src/mentat_cli/store.rs index 9bfc1e8d..f59a17ad 100644 --- a/tools/cli/src/mentat_cli/store.rs +++ b/tools/cli/src/mentat_cli/store.rs @@ -62,7 +62,24 @@ impl Store { Ok(self.conn.transact(&mut self.handle, &transaction)?) } + // the schema is the entire schema of the store including structure used to describe the store. pub fn fetch_schema(&self) -> edn::Value { self.conn.current_schema().to_edn_value() } + + // the attributes are the specific attributes added to the schema for this particular store. + pub fn fetch_attributes(&self) -> edn::Value { + let schema = self.conn.current_schema(); + + edn::Value::Vector((&schema.schema_map).iter() + .filter_map(|(entid, attribute)| { + if let Some(ident) = schema.get_ident(*entid) { + if !ident.namespace.starts_with("db") { + return Some(attribute.to_edn_value(Some(ident.clone()))); + } + } + return None; + }) + .collect()) + } }