From 26446ddb057b7616356fa20865f3f224611e5230 Mon Sep 17 00:00:00 2001 From: Grisha Kruglov Date: Wed, 6 Jun 2018 14:53:22 -0700 Subject: [PATCH] Add a top-level "syncable" feature. Tested with: cargo test --all cargo test --all --no-default-features cargo build --manifest-path tools/cli/Cargo.toml --no-default-features cargo run --manifest-path tools/cli/Cargo.toml --no-default-features debugcli Co-authored-by: Nick Alexander --- Cargo.toml | 4 +- src/errors.rs | 4 + src/lib.rs | 2 + src/store.rs | 2 + tests/tolstoy.rs | 270 ++++++++++++++++--------------- tools/cli/Cargo.toml | 3 +- tools/cli/src/mentat_cli/repl.rs | 30 +++- 7 files changed, 175 insertions(+), 140 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6be2c318..d6b0534b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,10 @@ version = "0.8.1" build = "build/version.rs" [features] -default = ["bundled_sqlite3"] +default = ["bundled_sqlite3", "syncable"] bundled_sqlite3 = ["rusqlite/bundled"] sqlcipher = ["rusqlite/sqlcipher", "mentat_db/sqlcipher"] +syncable = ["mentat_tolstoy"] [workspace] members = ["tools/cli", "ffi"] @@ -71,6 +72,7 @@ path = "query-translator" [dependencies.mentat_tolstoy] path = "tolstoy" +optional = true [profile.release] opt-level = 3 diff --git a/src/errors.rs b/src/errors.rs index 83a29dff..cffa3999 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -29,6 +29,8 @@ use mentat_query_algebrizer; use mentat_query_projector; use mentat_query_pull; use mentat_sql; + +#[cfg(feature = "syncable")] use mentat_tolstoy; pub type Result = std::result::Result; @@ -107,6 +109,7 @@ pub enum MentatError { #[fail(display = "{}", _0)] SQLError(#[cause] mentat_sql::SQLError), + #[cfg(feature = "syncable")] #[fail(display = "{}", _0)] TolstoyError(#[cause] mentat_tolstoy::TolstoyError), } @@ -159,6 +162,7 @@ impl From for MentatError { } } +#[cfg(feature = "syncable")] impl From for MentatError { fn from(error: mentat_tolstoy::TolstoyError) -> MentatError { MentatError::TolstoyError(error) diff --git a/src/lib.rs b/src/lib.rs index afd08817..9e12f38a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,8 @@ extern crate mentat_query_projector; extern crate mentat_query_pull; extern crate mentat_query_translator; extern crate mentat_sql; + +#[cfg(feature = "syncable")] extern crate mentat_tolstoy; pub use mentat_core::{ diff --git a/src/store.rs b/src/store.rs index e3b269e0..de86f6da 100644 --- a/src/store.rs +++ b/src/store.rs @@ -38,6 +38,7 @@ use mentat_db::{ TxObserver, }; +#[cfg(feature = "syncable")] use mentat_tolstoy::Syncer; use uuid::Uuid; @@ -237,6 +238,7 @@ impl Pullable for Store { } } +#[cfg(feature = "syncable")] impl Syncable for Store { fn sync(&mut self, server_uri: &String, user_uuid: &String) -> Result<()> { let uuid = Uuid::parse_str(&user_uuid).map_err(|_| MentatError::BadUuid(user_uuid.clone()))?; diff --git a/tests/tolstoy.rs b/tests/tolstoy.rs index 788dc442..fff655aa 100644 --- a/tests/tolstoy.rs +++ b/tests/tolstoy.rs @@ -10,148 +10,154 @@ extern crate mentat; extern crate mentat_core; + +#[cfg(feature = "syncable")] extern crate mentat_tolstoy; -use std::collections::BTreeMap; +#[cfg(feature = "syncable")] +mod tests { + use std::collections::BTreeMap; -use mentat::conn::Conn; + use mentat::conn::Conn; -use mentat::new_connection; -use mentat_tolstoy::tx_processor::{ - Processor, - TxReceiver, - TxPart, -}; -use mentat_tolstoy::errors::Result; -use mentat_core::{ - Entid, - TypedValue, - ValueType, -}; + use mentat::new_connection; + use mentat_tolstoy::tx_processor::{ + Processor, + TxReceiver, + TxPart, + }; + use mentat_tolstoy::errors::Result; + use mentat_core::{ + Entid, + TypedValue, + ValueType, + }; -struct TxCountingReceiver { - pub tx_count: usize, - pub is_done: bool, -} + struct TxCountingReceiver { + pub tx_count: usize, + pub is_done: bool, + } -impl TxCountingReceiver { - fn new() -> TxCountingReceiver { - TxCountingReceiver { - tx_count: 0, - is_done: false, + impl TxCountingReceiver { + fn new() -> TxCountingReceiver { + TxCountingReceiver { + tx_count: 0, + is_done: false, + } } } -} -impl TxReceiver for TxCountingReceiver { - fn tx(&mut self, _tx_id: Entid, _d: &mut T) -> Result<()> - where T: Iterator { - self.tx_count = self.tx_count + 1; - Ok(()) - } + impl TxReceiver for TxCountingReceiver { + fn tx(&mut self, _tx_id: Entid, _d: &mut T) -> Result<()> + where T: Iterator { + self.tx_count = self.tx_count + 1; + Ok(()) + } - fn done(&mut self) -> Result<()> { - self.is_done = true; - Ok(()) - } -} - -#[derive(Debug)] -struct TestingReceiver { - pub txes: BTreeMap>, - pub is_done: bool, -} - -impl TestingReceiver { - fn new() -> TestingReceiver { - TestingReceiver { - txes: BTreeMap::new(), - is_done: false, + fn done(&mut self) -> Result<()> { + self.is_done = true; + Ok(()) } } -} - -impl TxReceiver for TestingReceiver { - fn tx(&mut self, tx_id: Entid, d: &mut T) -> Result<()> - where T: Iterator { - let datoms = self.txes.entry(tx_id).or_insert(vec![]); - datoms.extend(d); - Ok(()) - } - - fn done(&mut self) -> Result<()> { - self.is_done = true; - Ok(()) - } -} - -fn assert_tx_datoms_count(receiver: &TestingReceiver, tx_num: usize, expected_datoms: usize) { - let tx = receiver.txes.keys().nth(tx_num).expect("first tx"); - let datoms = receiver.txes.get(tx).expect("datoms"); - assert_eq!(expected_datoms, datoms.len()); -} - -#[test] -fn test_reader() { - let mut c = new_connection("").expect("Couldn't open conn."); - let mut conn = Conn::connect(&mut c).expect("Couldn't open DB."); - { - let db_tx = c.transaction().expect("db tx"); - // Don't inspect the bootstrap transaction, but we'd like to see it's there. - let mut receiver = TxCountingReceiver::new(); - assert_eq!(false, receiver.is_done); - Processor::process(&db_tx, None, &mut receiver).expect("processor"); - assert_eq!(true, receiver.is_done); - assert_eq!(1, receiver.tx_count); - } - - let ids = conn.transact(&mut c, r#"[ - [:db/add "s" :db/ident :foo/numba] - [:db/add "s" :db/valueType :db.type/long] - [:db/add "s" :db/cardinality :db.cardinality/one] - ]"#).expect("successful transaction").tempids; - let numba_entity_id = ids.get("s").unwrap(); - - let bootstrap_tx; - { - let db_tx = c.transaction().expect("db tx"); - // Expect to see one more transaction of four parts (one for tx datom itself). - let mut receiver = TestingReceiver::new(); - Processor::process(&db_tx, None, &mut receiver).expect("processor"); - - println!("{:#?}", receiver); - - assert_eq!(2, receiver.txes.keys().count()); - assert_tx_datoms_count(&receiver, 1, 4); - - bootstrap_tx = Some(*receiver.txes.keys().nth(0).expect("bootstrap tx")); - } - - let ids = conn.transact(&mut c, r#"[ - [:db/add "b" :foo/numba 123] - ]"#).expect("successful transaction").tempids; - let asserted_e = ids.get("b").unwrap(); - - { - let db_tx = c.transaction().expect("db tx"); - - // Expect to see a single two part transaction - let mut receiver = TestingReceiver::new(); - - // Note that we're asking for the bootstrap tx to be skipped by the processor. - Processor::process(&db_tx, bootstrap_tx, &mut receiver).expect("processor"); - - assert_eq!(2, receiver.txes.keys().count()); - assert_tx_datoms_count(&receiver, 1, 2); - - // Inspect the transaction part. - let tx_id = receiver.txes.keys().nth(1).expect("tx"); - let datoms = receiver.txes.get(tx_id).expect("datoms"); - let part = datoms.iter().find(|&part| &part.e == asserted_e).expect("to find asserted datom"); - - assert_eq!(numba_entity_id, &part.a); - assert!(part.v.matches_type(ValueType::Long)); - assert_eq!(TypedValue::Long(123), part.v); - assert_eq!(true, part.added); - } + + #[derive(Debug)] + struct TestingReceiver { + pub txes: BTreeMap>, + pub is_done: bool, + } + + impl TestingReceiver { + fn new() -> TestingReceiver { + TestingReceiver { + txes: BTreeMap::new(), + is_done: false, + } + } + } + + impl TxReceiver for TestingReceiver { + fn tx(&mut self, tx_id: Entid, d: &mut T) -> Result<()> + where T: Iterator { + let datoms = self.txes.entry(tx_id).or_insert(vec![]); + datoms.extend(d); + Ok(()) + } + + fn done(&mut self) -> Result<()> { + self.is_done = true; + Ok(()) + } + } + + fn assert_tx_datoms_count(receiver: &TestingReceiver, tx_num: usize, expected_datoms: usize) { + let tx = receiver.txes.keys().nth(tx_num).expect("first tx"); + let datoms = receiver.txes.get(tx).expect("datoms"); + assert_eq!(expected_datoms, datoms.len()); + } + + #[test] + fn test_reader() { + let mut c = new_connection("").expect("Couldn't open conn."); + let mut conn = Conn::connect(&mut c).expect("Couldn't open DB."); + { + let db_tx = c.transaction().expect("db tx"); + // Don't inspect the bootstrap transaction, but we'd like to see it's there. + let mut receiver = TxCountingReceiver::new(); + assert_eq!(false, receiver.is_done); + Processor::process(&db_tx, None, &mut receiver).expect("processor"); + assert_eq!(true, receiver.is_done); + assert_eq!(1, receiver.tx_count); + } + + let ids = conn.transact(&mut c, r#"[ + [:db/add "s" :db/ident :foo/numba] + [:db/add "s" :db/valueType :db.type/long] + [:db/add "s" :db/cardinality :db.cardinality/one] + ]"#).expect("successful transaction").tempids; + let numba_entity_id = ids.get("s").unwrap(); + + let bootstrap_tx; + { + let db_tx = c.transaction().expect("db tx"); + // Expect to see one more transaction of four parts (one for tx datom itself). + let mut receiver = TestingReceiver::new(); + Processor::process(&db_tx, None, &mut receiver).expect("processor"); + + println!("{:#?}", receiver); + + assert_eq!(2, receiver.txes.keys().count()); + assert_tx_datoms_count(&receiver, 1, 4); + + bootstrap_tx = Some(*receiver.txes.keys().nth(0).expect("bootstrap tx")); + } + + let ids = conn.transact(&mut c, r#"[ + [:db/add "b" :foo/numba 123] + ]"#).expect("successful transaction").tempids; + let asserted_e = ids.get("b").unwrap(); + + { + let db_tx = c.transaction().expect("db tx"); + + // Expect to see a single two part transaction + let mut receiver = TestingReceiver::new(); + + // Note that we're asking for the bootstrap tx to be skipped by the processor. + Processor::process(&db_tx, bootstrap_tx, &mut receiver).expect("processor"); + + assert_eq!(2, receiver.txes.keys().count()); + assert_tx_datoms_count(&receiver, 1, 2); + + // Inspect the transaction part. + let tx_id = receiver.txes.keys().nth(1).expect("tx"); + let datoms = receiver.txes.get(tx_id).expect("datoms"); + let part = datoms.iter().find(|&part| &part.e == asserted_e).expect("to find asserted datom"); + + assert_eq!(numba_entity_id, &part.a); + assert!(part.v.matches_type(ValueType::Long)); + assert_eq!(TypedValue::Long(123), part.v); + assert_eq!(true, part.added); + } + } + } diff --git a/tools/cli/Cargo.toml b/tools/cli/Cargo.toml index 0c4ec2f7..126d1a75 100644 --- a/tools/cli/Cargo.toml +++ b/tools/cli/Cargo.toml @@ -4,9 +4,10 @@ version = "0.0.1" # Forward mentat's features. [features] -default = ["bundled_sqlite3"] +default = ["bundled_sqlite3", "syncable"] sqlcipher = ["mentat/sqlcipher"] bundled_sqlite3 = ["mentat/bundled_sqlite3"] +syncable = ["mentat/syncable"] [lib] name = "mentat_cli" diff --git a/tools/cli/src/mentat_cli/repl.rs b/tools/cli/src/mentat_cli/repl.rs index 6c3564e0..2a75440c 100644 --- a/tools/cli/src/mentat_cli/repl.rs +++ b/tools/cli/src/mentat_cli/repl.rs @@ -35,19 +35,23 @@ use mentat_core::{ }; use mentat::{ + Binding, CacheDirection, Keyword, - Queryable, QueryExplanation, QueryOutput, QueryResults, + Queryable, Store, - Binding, - Syncable, TxReport, TypedValue, }; +#[cfg(feature = "syncable")] +use mentat::{ + Syncable, +}; + use command_parser::{ Command, }; @@ -66,7 +70,6 @@ use command_parser::{ COMMAND_QUERY_EXPLAIN_SHORT, COMMAND_QUERY_PREPARED_LONG, COMMAND_SCHEMA, - COMMAND_SYNC, COMMAND_TIMER_LONG, COMMAND_TRANSACT_LONG, COMMAND_TRANSACT_SHORT, @@ -82,6 +85,11 @@ use command_parser::{ COMMAND_OPEN_ENCRYPTED, }; +#[cfg(feature = "syncable")] +use command_parser::{ + COMMAND_SYNC, +}; + use input::InputReader; use input::InputResult::{ Empty, @@ -124,7 +132,9 @@ lazy_static! { (COMMAND_TIMER_LONG, "Enable or disable timing of query and transact operations."), (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."), + + #[cfg(feature = "syncable")] + (COMMAND_SYNC, "Synchronize the database against a Mentat Sync Server URL for a provided user UUID."), ] }; } @@ -359,12 +369,20 @@ impl Repl { Err(e) => eprintln!("{}", e) }; }, + + #[cfg(feature = "syncable")] Command::Sync(args) => { match self.store.sync(&args[0], &args[1]) { Ok(_) => println!("Synced!"), Err(e) => eprintln!("{:?}", e) }; - } + }, + + #[cfg(not(feature = "syncable"))] + Command::Sync(_) => { + eprintln!(".sync requires the syncable Mentat feature"); + }, + Command::Timer(on) => { self.toggle_timer(on); },