Skip to content
This repository has been archived by the owner on Sep 12, 2018. It is now read-only.

Commit

Permalink
Patch it all together: use MentatError at top-level.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ncalexan committed Jun 27, 2018
1 parent 3a69b8a commit 60aa80d
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 36 deletions.
30 changes: 15 additions & 15 deletions src/conn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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),
Expand All @@ -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));
},
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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"),
Expand All @@ -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),
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/entity_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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."),
Expand Down
88 changes: 86 additions & 2 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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),

Expand Down Expand Up @@ -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)
}
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 ident;
Expand Down
4 changes: 2 additions & 2 deletions src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?)
}
}
Expand Down
22 changes: 11 additions & 11 deletions tests/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
},
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
},
Expand All @@ -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));
Expand Down Expand Up @@ -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);
},
Expand Down
4 changes: 2 additions & 2 deletions tests/vocabulary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions tools/cli/src/mentat_cli/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use std::io::Write;

use failure::{
err_msg,
Error,
};

Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 60aa80d

Please sign in to comment.