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:
parent
ac1b0b15fe
commit
d31ec28aa8
8 changed files with 124 additions and 36 deletions
30
src/conn.rs
30
src/conn.rs
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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."),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue