Patch it all together: use MentatError at top-level.

I elected to keep Tolstoy using `failure::Error`, because Tolstoy
looks rather more like a high-level application (and will continue to
do so for a while) than a production-ready mid- or low-level API.
This commit is contained in:
Nick Alexander 2018-06-27 13:19:40 -07:00
parent ac1b0b15fe
commit d31ec28aa8
8 changed files with 124 additions and 36 deletions

View file

@ -185,7 +185,7 @@ pub trait Pullable {
}
pub trait Syncable {
fn sync(&mut self, server_uri: &String, user_uuid: &String) -> Result<()>;
fn sync(&mut self, server_uri: &String, user_uuid: &String) -> ::std::result::Result<(), ::failure::Error>;
}
/// Represents an in-progress, not yet committed, set of changes to the store.
@ -871,8 +871,8 @@ mod tests {
.partition_map[":db.part/user"].index;
let t = format!("[[:db/add {} :db.schema/attribute \"tempid\"]]", next + 1);
match conn.transact(&mut sqlite, t.as_str()).expect_err("expected transact error").downcast() {
Ok(e @ ::mentat_db::DbError { .. }) => {
match conn.transact(&mut sqlite, t.as_str()) {
Err(MentatError::DbError(e)) => {
assert_eq!(e.kind(), ::mentat_db::DbErrorKind::UnrecognizedEntid(next + 1));
},
x => panic!("expected db error, got {:?}", x),
@ -898,8 +898,8 @@ mod tests {
// we should reject this, because the first ID was provided by the user!
let t = format!("[[:db/add {} :db.schema/attribute \"tempid\"]]", next);
match conn.transact(&mut sqlite, t.as_str()).expect_err("expected transact error").downcast() {
Ok(e @ ::mentat_db::DbError { .. }) => {
match conn.transact(&mut sqlite, t.as_str()) {
Err(MentatError::DbError(e)) => {
// All this, despite this being the ID we were about to allocate!
assert_eq!(e.kind(), ::mentat_db::DbErrorKind::UnrecognizedEntid(next));
},
@ -1059,9 +1059,9 @@ mod tests {
// Bad EDN: missing closing ']'.
let report = conn.transact(&mut sqlite, "[[:db/add \"t\" :db/ident :a/keyword]");
match report.expect_err("expected transact to fail for bad edn").downcast() {
Ok(edn::ParseError { .. }) => { },
Err(x) => panic!("expected EDN parse error, got {:?}", x),
match report.expect_err("expected transact to fail for bad edn") {
MentatError::EdnParseError(_) => { },
x => panic!("expected EDN parse error, got {:?}", x),
}
// Good EDN.
@ -1070,9 +1070,9 @@ mod tests {
// Bad transaction data: missing leading :db/add.
let report = conn.transact(&mut sqlite, "[[\"t\" :db/ident :b/keyword]]");
match report.expect_err("expected transact error").downcast() {
Ok(edn::ParseError { .. }) => { },
Err(x) => panic!("expected EDN parse error, got {:?}", x),
match report.expect_err("expected transact error") {
MentatError::EdnParseError(_) => { },
x => panic!("expected EDN parse error, got {:?}", x),
}
// Good transaction data.
@ -1082,8 +1082,8 @@ mod tests {
// Bad transaction based on state of store: conflicting upsert.
let report = conn.transact(&mut sqlite, "[[:db/add \"u\" :db/ident :a/keyword]
[:db/add \"u\" :db/ident :b/keyword]]");
match report.expect_err("expected transact error").downcast() {
Ok(e @ ::mentat_db::DbError { .. }) => {
match report.expect_err("expected transact error") {
MentatError::DbError(e) => {
match e.kind() {
::mentat_db::DbErrorKind::SchemaConstraintViolation(_) => {},
_ => panic!("expected SchemaConstraintViolation"),
@ -1106,8 +1106,8 @@ mod tests {
let kw = kw!(:foo/bat);
let schema = conn.current_schema();
let res = conn.cache(&mut sqlite, &schema, &kw, CacheDirection::Forward, CacheAction::Register);
match res.expect_err("expected cache to fail").downcast() {
Ok(MentatError::UnknownAttribute(msg)) => assert_eq!(msg, ":foo/bat"),
match res.expect_err("expected cache to fail") {
MentatError::UnknownAttribute(msg) => assert_eq!(msg, ":foo/bat"),
x => panic!("expected UnknownAttribute error, got {:?}", x),
}
}

View file

@ -419,8 +419,8 @@ mod testing {
let mut in_progress = conn.begin_transaction(&mut sqlite).expect("begun successfully");
// This should fail: unrecognized entid.
match in_progress.transact_terms(terms, tempids).expect_err("expected transact to fail").downcast() {
Ok(e @ mentat_db::DbError { .. }) => {
match in_progress.transact_terms(terms, tempids).expect_err("expected transact to fail") {
MentatError::DbError(e) => {
assert_eq!(e.kind(), mentat_db::DbErrorKind::UnrecognizedEntid(999));
},
_ => panic!("Should have rejected the entid."),

View file

@ -14,16 +14,23 @@ use std; // To refer to std::result::Result.
use std::collections::BTreeSet;
use failure::Error;
use rusqlite;
use edn;
use mentat_core::{
Attribute,
ValueType,
};
use mentat_db;
use mentat_query;
use mentat_query_algebrizer;
use mentat_query_projector;
use mentat_query_pull;
use mentat_sql;
pub type Result<T> = std::result::Result<T, Error>;
pub type Result<T> = std::result::Result<T, MentatError>;
#[macro_export]
macro_rules! bail {
@ -34,6 +41,9 @@ macro_rules! bail {
#[derive(Debug, Fail)]
pub enum MentatError {
#[fail(display = "bad uuid {}", _0)]
BadUuid(String),
#[fail(display = "path {} already exists", _0)]
PathAlreadyExists(String),
@ -69,4 +79,78 @@ pub enum MentatError {
#[fail(display = "provided value of type {} doesn't match attribute value type {}", _0, _1)]
ValueTypeMismatch(ValueType, ValueType),
#[fail(display = "{}", _0)]
IoError(#[cause] std::io::Error),
// It would be better to capture the underlying `rusqlite::Error`, but that type doesn't
// implement many useful traits, including `Clone`, `Eq`, and `PartialEq`.
#[fail(display = "SQL error: _0")]
RusqliteError(String),
#[fail(display = "{}", _0)]
EdnParseError(#[cause] edn::ParseError),
#[fail(display = "{}", _0)]
DbError(#[cause] mentat_db::DbError),
#[fail(display = "{}", _0)]
AlgebrizerError(#[cause] mentat_query_algebrizer::AlgebrizerError),
#[fail(display = "{}", _0)]
ProjectorError(#[cause] mentat_query_projector::ProjectorError),
#[fail(display = "{}", _0)]
PullError(#[cause] mentat_query_pull::PullError),
#[fail(display = "{}", _0)]
SQLError(#[cause] mentat_sql::SQLError),
}
impl From<std::io::Error> for MentatError {
fn from(error: std::io::Error) -> MentatError {
MentatError::IoError(error)
}
}
impl From<rusqlite::Error> for MentatError {
fn from(error: rusqlite::Error) -> MentatError {
MentatError::RusqliteError(error.to_string())
}
}
impl From<edn::ParseError> for MentatError {
fn from(error: edn::ParseError) -> MentatError {
MentatError::EdnParseError(error)
}
}
impl From<mentat_db::DbError> for MentatError {
fn from(error: mentat_db::DbError) -> MentatError {
MentatError::DbError(error)
}
}
impl From<mentat_query_algebrizer::AlgebrizerError> for MentatError {
fn from(error: mentat_query_algebrizer::AlgebrizerError) -> MentatError {
MentatError::AlgebrizerError(error)
}
}
impl From<mentat_query_projector::ProjectorError> for MentatError {
fn from(error: mentat_query_projector::ProjectorError) -> MentatError {
MentatError::ProjectorError(error)
}
}
impl From<mentat_query_pull::PullError> for MentatError {
fn from(error: mentat_query_pull::PullError) -> MentatError {
MentatError::PullError(error)
}
}
impl From<mentat_sql::SQLError> for MentatError {
fn from(error: mentat_sql::SQLError) -> MentatError {
MentatError::SQLError(error)
}
}

View file

@ -104,6 +104,10 @@ macro_rules! kw {
#[macro_use]
pub mod errors;
pub use errors::{
MentatError,
Result,
};
pub mod conn;
pub mod entity_builder;
pub mod query;

View file

@ -234,8 +234,8 @@ impl Pullable for Store {
}
impl Syncable for Store {
fn sync(&mut self, server_uri: &String, user_uuid: &String) -> Result<()> {
let uuid = Uuid::parse_str(&user_uuid)?;
fn sync(&mut self, server_uri: &String, user_uuid: &String) -> ::std::result::Result<(), ::failure::Error> {
let uuid = Uuid::parse_str(&user_uuid).map_err(|_| MentatError::BadUuid(user_uuid.clone()))?;
Ok(Syncer::flow(&mut self.sqlite, server_uri, &uuid)?)
}
}

View file

@ -233,7 +233,7 @@ fn test_unbound_inputs() {
let results = q_uncached(&c, &db.schema,
"[:find ?i . :in ?e :where [?e :db/ident ?i]]", inputs);
match results.expect_err("expected unbound variables").downcast().expect("expected specific error") {
match results.expect_err("expected unbound variables") {
MentatError::UnboundVariables(vars) => {
assert_eq!(vars, vec!["?e".to_string()].into_iter().collect());
},
@ -411,8 +411,8 @@ fn test_fulltext() {
[?a :foo/term ?term]
]"#;
let r = conn.q_once(&mut c, query, None);
match r.expect_err("expected query to fail").downcast() {
Ok(mentat_query_algebrizer::AlgebrizerError::InvalidArgument(PlainSymbol(s), ty, i)) => {
match r.expect_err("expected query to fail") {
MentatError::AlgebrizerError(mentat_query_algebrizer::AlgebrizerError::InvalidArgument(PlainSymbol(s), ty, i)) => {
assert_eq!(s, "fulltext");
assert_eq!(ty, "string");
assert_eq!(i, 2);
@ -426,8 +426,8 @@ fn test_fulltext() {
[?a :foo/term ?term]
[(fulltext $ :foo/fts ?a) [[?x ?val]]]]"#;
let r = conn.q_once(&mut c, query, None);
match r.expect_err("expected query to fail").downcast() {
Ok(mentat_query_algebrizer::AlgebrizerError::InvalidArgument(PlainSymbol(s), ty, i)) => {
match r.expect_err("expected query to fail") {
MentatError::AlgebrizerError(mentat_query_algebrizer::AlgebrizerError::InvalidArgument(PlainSymbol(s), ty, i)) => {
assert_eq!(s, "fulltext");
assert_eq!(ty, "string");
assert_eq!(i, 2);
@ -582,8 +582,8 @@ fn test_aggregates_type_handling() {
// No type limits => can't do it.
let r = store.q_once(r#"[:find (sum ?v) . :where [_ _ ?v]]"#, None);
let all_types = ValueTypeSet::any();
match r.expect_err("expected query to fail").downcast() {
Ok(::mentat_query_projector::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
match r.expect_err("expected query to fail") {
MentatError::ProjectorError(::mentat_query_projector::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
SimpleAggregationOp::Sum, types)) => {
assert_eq!(types, all_types);
},
@ -594,8 +594,8 @@ fn test_aggregates_type_handling() {
let r = store.q_once(r#"[:find (sum ?v) .
:where [_ _ ?v] [(type ?v :db.type/instant)]]"#,
None);
match r.expect_err("expected query to fail").downcast() {
Ok(::mentat_query_projector::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
match r.expect_err("expected query to fail") {
MentatError::ProjectorError(::mentat_query_projector::errors::ProjectorError::CannotApplyAggregateOperationToTypes(
SimpleAggregationOp::Sum,
types)) => {
assert_eq!(types, ValueTypeSet::of_one(ValueType::Instant));
@ -1336,8 +1336,8 @@ fn test_aggregation_implicit_grouping() {
[?person :foo/play ?game]
[?person :foo/is-vegetarian true]
[?person :foo/name ?name]]"#, None);
match res.expect_err("expected query to fail").downcast() {
Ok(::mentat_query_projector::errors::ProjectorError::AmbiguousAggregates(mmc, cc)) => {
match res.expect_err("expected query to fail") {
MentatError::ProjectorError(::mentat_query_projector::errors::ProjectorError::AmbiguousAggregates(mmc, cc)) => {
assert_eq!(mmc, 2);
assert_eq!(cc, 1);
},

View file

@ -286,8 +286,8 @@ fn test_add_vocab() {
// Scoped borrow of `conn`.
{
let mut in_progress = conn.begin_transaction(&mut sqlite).expect("begun successfully");
match in_progress.ensure_vocabulary(&foo_v1_malformed).expect_err("expected vocabulary to fail").downcast() {
Ok(MentatError::ConflictingAttributeDefinitions(vocab, version, attr, theirs, ours)) => {
match in_progress.ensure_vocabulary(&foo_v1_malformed).expect_err("expected vocabulary to fail") {
MentatError::ConflictingAttributeDefinitions(vocab, version, attr, theirs, ours) => {
assert_eq!(vocab.as_str(), ":org.mozilla/foo");
assert_eq!(attr.as_str(), ":foo/baz");
assert_eq!(version, 1);

View file

@ -11,7 +11,6 @@
use std::io::Write;
use failure::{
err_msg,
Error,
};
@ -404,7 +403,8 @@ impl Repl {
if self.path.is_empty() || path != self.path {
let next = match encryption_key {
#[cfg(not(feature = "sqlcipher"))]
Some(_) => return Err(err_msg(".open_encrypted and .empty_encrypted require the sqlcipher Mentat feature")),
Some(_) => return Err(::mentat::MentatError::RusqliteError(".open_encrypted and .empty_encrypted require the sqlcipher Mentat feature".into())),
#[cfg(feature = "sqlcipher")]
Some(k) => {
if empty {