diff --git a/Cargo.lock b/Cargo.lock index d0b9245..e3b0c87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -247,6 +247,7 @@ dependencies = [ "subduction_core", "subduction_crypto", "subduction_websocket", + "thiserror", "tokio", "tracing", "tracing-subscriber", diff --git a/packages/coln-store/Cargo.toml b/packages/coln-store/Cargo.toml index 58c97f7..d7e3e1b 100644 --- a/packages/coln-store/Cargo.toml +++ b/packages/coln-store/Cargo.toml @@ -29,6 +29,7 @@ rustyline = { version = "18.0.0", optional = true } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.149" shlex = "2.0.1" +thiserror = "2.0.18" tracing = "0.1" tracing-subscriber = { version = "0.3", features = [ "env-filter", diff --git a/packages/coln-store/src/commit/graph.rs b/packages/coln-store/src/commit/graph.rs index 95f5873..51e76f9 100644 --- a/packages/coln-store/src/commit/graph.rs +++ b/packages/coln-store/src/commit/graph.rs @@ -2,11 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use std::{ - collections::{BTreeSet, HashMap}, - error::Error, - fmt, -}; +use std::collections::{BTreeSet, HashMap}; use crate::commit::{Commit, CommitHash}; @@ -23,25 +19,14 @@ pub struct CommitGraph { heads: BTreeSet, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] pub enum CommitGraphError { + #[error("commit graph has no root commit")] MissingRoot, + #[error("commit graph has multiple root commits")] MultipleRoots, } -impl fmt::Display for CommitGraphError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - CommitGraphError::MissingRoot => write!(f, "commit graph has no root commit"), - CommitGraphError::MultipleRoots => { - write!(f, "commit graph has multiple root commits") - } - } - } -} - -impl Error for CommitGraphError {} - impl CommitGraph { pub fn new() -> Self { Self::default() diff --git a/packages/coln-store/src/commit/pst.rs b/packages/coln-store/src/commit/pst.rs index fda262b..ecb83a1 100644 --- a/packages/coln-store/src/commit/pst.rs +++ b/packages/coln-store/src/commit/pst.rs @@ -39,7 +39,7 @@ pub fn encode_store(store: &Store) -> Result, CodecError> { } /// Decode a store from bytes produced by [`encode_store`]. -pub fn decode_store(data: &[u8]) -> Result> { +pub fn decode_store(data: &[u8]) -> Result { let encoded = read_store_envelope(data)?; decode_store_chunks(encoded.next_oid, encoded.chunks) } @@ -89,10 +89,7 @@ fn write_commit_chunk(buf: &mut Vec, commit: &Commit<'_>) { Chunk::from(commit).write(buf) } -fn decode_store_chunks( - next_oid: TableOid, - chunks: Vec, -) -> Result> { +fn decode_store_chunks(next_oid: TableOid, chunks: Vec) -> Result { let roots = chunks .iter() .filter(|chunk| chunk.chunk_type() == ChunkType::Root) @@ -193,12 +190,12 @@ mod tests { .collect() } - fn is_data_format_error(result: Result>) -> bool { + fn is_data_format_error(result: Result) -> bool { matches!( result, Err(err) if matches!( - err.as_ref(), + &err, StoreIntError::Encode(CodecError::DataFormatError(_)) ) ) @@ -215,12 +212,12 @@ mod tests { .expect("encoded store contains a chunk magic") } - fn is_missing_dep_error(result: Result>) -> bool { + fn is_missing_dep_error(result: Result) -> bool { matches!( result, Err(err) if matches!( - err.as_ref(), + &err, StoreIntError::Commit(crate::store::error::CommitApplyError::MissingDep) ) ) @@ -310,7 +307,7 @@ mod tests { let err = decode_store(&corrupted).expect_err("checksum mismatch"); assert!(matches!( - err.as_ref(), + err, StoreIntError::Encode(CodecError::ChecksumMismatch) )); } diff --git a/packages/coln-store/src/repl/error.rs b/packages/coln-store/src/repl/error.rs index fbf4e8b..da520b0 100644 --- a/packages/coln-store/src/repl/error.rs +++ b/packages/coln-store/src/repl/error.rs @@ -18,7 +18,7 @@ pub enum ReplError { BadValue { column: usize, message: String }, Io(std::io::Error), Json(serde_json::Error), - Store(Box), + Store(StoreIntError), Persist(CodecError), } @@ -64,12 +64,6 @@ impl From for ReplError { impl From for ReplError { fn from(value: StoreIntError) -> Self { - Self::Store(Box::new(value)) - } -} - -impl From> for ReplError { - fn from(value: Box) -> Self { Self::Store(value) } } @@ -82,7 +76,7 @@ impl From for ReplError { impl From for ReplError { fn from(value: ValidationError) -> Self { - Self::Store(Box::new(value.into())) + Self::Store(value.into()) } } diff --git a/packages/coln-store/src/solver/compile.rs b/packages/coln-store/src/solver/compile.rs index 938bfd2..c1e0e1a 100644 --- a/packages/coln-store/src/solver/compile.rs +++ b/packages/coln-store/src/solver/compile.rs @@ -7,11 +7,15 @@ use std::{collections::HashSet, fmt}; use crate::ir::{self, Atom, Prop, RuleEntry, Term}; /// Errors raised while lowering an `ir::Rule` into the restricted solver form. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] pub enum CompileError { + #[error("unsupported prop {0}")] UnsupportedProp(String), + #[error("unsupported term")] UnsupportedTerm, + #[error("invalid var index: {index} var_count {var_count}")] InvalidVarIndex { index: i64, var_count: usize }, + #[error("invalid column index {column}")] InvalidColumnIndex { column: i64 }, } diff --git a/packages/coln-store/src/store/error.rs b/packages/coln-store/src/store/error.rs index ac0e9c5..deffa89 100644 --- a/packages/coln-store/src/store/error.rs +++ b/packages/coln-store/src/store/error.rs @@ -2,126 +2,37 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use std::error::Error; -use std::fmt; - use crate::commit::error::CodecError; use crate::solver::compile::CompileError; use crate::solver::validate::RuleViolation; use crate::table::ValidationError; /// Store integrity error -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum StoreIntError { - Validation(ValidationError), - Law(Box), - Compile(CompileError), - Encode(CodecError), - Commit(CommitApplyError), -} - -#[derive(Debug)] + #[error(transparent)] + Validation(#[from] ValidationError), + #[error(transparent)] + Law(#[from] Box), + #[error(transparent)] + Compile(#[from] CompileError), + #[error(transparent)] + Encode(#[from] CodecError), + #[error(transparent)] + Commit(#[from] CommitApplyError), +} + +#[derive(Debug, thiserror::Error)] pub enum CommitApplyError { + #[error("missing commit dependency")] MissingDep, + #[error("disconnected commit")] DisconnectedCommit, // A commit that should definitely exist but is missing + #[error("missing commit")] MissingCommit, + #[error("An existing commit has conflict payload")] ConflictPayload, + #[error("Root commit cannot be applied")] RootCommit, } - -impl fmt::Display for CommitApplyError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - CommitApplyError::MissingDep => write!(f, "missing commit dependency"), - CommitApplyError::DisconnectedCommit => write!(f, "disconnected commit"), - CommitApplyError::MissingCommit => write!(f, "missing commit"), - CommitApplyError::ConflictPayload => { - write!(f, "An existing commit has conflict payload") - } - CommitApplyError::RootCommit => write!(f, "Root commit cannot be applied"), - } - } -} - -impl Error for CommitApplyError {} - -impl fmt::Display for StoreIntError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - StoreIntError::Validation(err) => write!(f, "{err}"), - StoreIntError::Law(err) => write!(f, "{err}"), - StoreIntError::Compile(err) => write!(f, "{err:?}"), - StoreIntError::Encode(err) => write!(f, "{err:?}"), - StoreIntError::Commit(err) => write!(f, "{err}"), - } - } -} - -impl Error for StoreIntError {} - -impl From for StoreIntError { - fn from(value: ValidationError) -> Self { - Self::Validation(value) - } -} - -impl From for StoreIntError { - fn from(value: RuleViolation) -> Self { - Self::Law(Box::new(value)) - } -} - -impl From> for StoreIntError { - fn from(value: Box) -> Self { - Self::Law(value) - } -} - -impl From for StoreIntError { - fn from(value: CompileError) -> Self { - Self::Compile(value) - } -} - -impl From for StoreIntError { - fn from(value: CommitApplyError) -> Self { - Self::Commit(value) - } -} - -impl From for Box { - fn from(value: CodecError) -> Self { - Box::new(StoreIntError::Encode(value)) - } -} - -impl From for Box { - fn from(value: CommitApplyError) -> Self { - Box::new(StoreIntError::from(value)) - } -} - -impl From for Box { - fn from(value: ValidationError) -> Self { - Box::new(StoreIntError::from(value)) - } -} - -impl From for Box { - fn from(value: CompileError) -> Self { - Box::new(StoreIntError::from(value)) - } -} - -impl From for Box { - fn from(value: RuleViolation) -> Self { - Box::new(StoreIntError::from(value)) - } -} - -impl From> for Box { - fn from(value: Box) -> Self { - Box::new(StoreIntError::from(value)) - } -} diff --git a/packages/coln-store/src/store/mod.rs b/packages/coln-store/src/store/mod.rs index faeb057..b4c86df 100644 --- a/packages/coln-store/src/store/mod.rs +++ b/packages/coln-store/src/store/mod.rs @@ -190,7 +190,7 @@ impl Store { /// This is a low level API that mutates `self` before law checking, so /// callers that need atomicity must call it on a preview clone and publish /// that clone only after success. - fn apply_batch(&mut self, ops: Vec) -> Result<(), Box> { + fn apply_batch(&mut self, ops: Vec) -> Result<(), StoreIntError> { info!(op_count = ops.len(), "applying batch"); self.validate_batch(&ops)?; @@ -251,7 +251,7 @@ impl Store { /// Builds an empty column store per `theory.tables` and keeps only `theory.rules` /// (schemas are stored on each [`Table`]). - pub fn try_from_theory(theory: FlatRealm) -> Result> { + pub fn try_from_theory(theory: FlatRealm) -> Result { let FlatRealm { tables, rules } = theory; info!( table_count = tables.len(), @@ -300,7 +300,7 @@ impl Store { Ok(comp) } - pub fn check_laws(&self) -> Result<(), Box> { + pub fn check_laws(&self) -> Result<(), StoreIntError> { debug!(rule_count = self.rules.len(), "checking rules"); self.rules() .iter() @@ -374,26 +374,26 @@ impl Store { .collect() } - pub fn merge(&mut self, other: &Self) -> Result, Box> { + pub fn merge(&mut self, other: &Self) -> Result, StoreIntError> { let commits = self.commits_added(other); self.apply_commits(commits)?; Ok(self.heads()) } - fn apply_commit_ready(&mut self, cmt: Commit<'static>) -> Result<(), Box> { + fn apply_commit_ready(&mut self, cmt: Commit<'static>) -> Result<(), StoreIntError> { self.apply_batch(cmt.resolved_ops())?; self.record_in_commit_graph(cmt); Ok(()) } - pub fn apply_commit(&mut self, commit: Commit<'static>) -> Result<(), Box> { + pub fn apply_commit(&mut self, commit: Commit<'static>) -> Result<(), StoreIntError> { self.apply_commits([commit]) } pub fn apply_commits( &mut self, commits: impl IntoIterator>, - ) -> Result<(), Box> { + ) -> Result<(), StoreIntError> { let mut pending = HashMap::new(); for commit in commits { @@ -513,7 +513,7 @@ impl Store { pub fn apply_chunk_bytes( &mut self, chunk_bytes: impl IntoIterator>, - ) -> Result<(), Box> { + ) -> Result<(), StoreIntError> { let commits = chunk_bytes .into_iter() .map(|bytes| Chunk::decode(&bytes)) @@ -772,7 +772,7 @@ mod tests { }; assert!(matches!( - *err, + err, StoreIntError::Validation(ValidationError::UnknownTable { .. }) )); assert_eq!(store.table_at(&path).expect("T").row_count(), 0); @@ -802,7 +802,7 @@ mod tests { let err = txn.commit().unwrap_err(); assert!(matches!( - *err, + err, StoreIntError::Validation(ValidationError::DuplicatePrimaryKey) )); assert_eq!(store.table_at(&path).expect("T").row_count(), 0); @@ -994,7 +994,7 @@ mod tests { let err = target.apply_commits([second_commit]).unwrap_err(); assert!(matches!( - *err, + err, StoreIntError::Commit(CommitApplyError::MissingDep) )); assert_eq!( @@ -1020,7 +1020,7 @@ mod tests { .expect("add"); let err = txn.commit().unwrap_err(); - assert!(matches!(*err, StoreIntError::Law(_))); + assert!(matches!(err, StoreIntError::Law(_))); assert_eq!(store.table_at(&link).expect("Link").row_count(), 0); } @@ -1054,7 +1054,7 @@ mod tests { }), binding: vec![], }; - let law = StoreIntError::from(violation); + let law = StoreIntError::from(Box::new(violation)); assert!(matches!(law, StoreIntError::Law(_))); } } diff --git a/packages/coln-store/src/table.rs b/packages/coln-store/src/table.rs index fae9b92..7949ba9 100644 --- a/packages/coln-store/src/table.rs +++ b/packages/coln-store/src/table.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use std::collections::HashMap; -use std::error::Error; use std::fmt; use std::fmt::Write; @@ -31,88 +30,36 @@ impl fmt::Display for RowId { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, thiserror::Error)] pub enum ValidationError { - ColumnCount { - expected: usize, - got: usize, - }, + #[error("column count mismatch: expected {expected}, got {got}")] + ColumnCount { expected: usize, got: usize }, + #[error("type mismatch at column {column}: expected {expected}, got {got}")] TypeMismatch { column: usize, expected: &'static str, got: &'static str, }, - UnsupportedTuple { - column: usize, - }, - InvalidPrimaryKeyName { - name: ColName, - }, + #[error("tuple columns are not supported yet (column {column})")] + UnsupportedTuple { column: usize }, + #[error("primary key references unknown column: {name}")] + InvalidPrimaryKeyName { name: ColName }, + #[error("duplicate primary key")] DuplicatePrimaryKey, /// No table registered for this path (e.g. batch apply). - UnknownTable { - path: ir::Path, - }, + #[error("unknown table: {path:?}")] + UnknownTable { path: ir::Path }, + #[error("table mismatch: expected: {expected:?}, actual: {actual:?}")] TableMismatch { expected: ir::Path, actual: ir::Path, }, - TxnIdMismatch { - current: TxnId, - got: TxnId, - }, - InvalidRowHandle { - reason: String, - }, -} - -impl fmt::Display for ValidationError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ValidationError::ColumnCount { expected, got } => { - write!(f, "column count mismatch: expected {expected}, got {got}") - } - ValidationError::TypeMismatch { - column, - expected, - got, - } => write!( - f, - "type mismatch at column {column}: expected {expected}, got {got}" - ), - ValidationError::UnsupportedTuple { column } => { - write!(f, "tuple columns are not supported yet (column {column})") - } - ValidationError::InvalidPrimaryKeyName { name } => { - write!(f, "primary key references unknown column: {name}") - } - ValidationError::DuplicatePrimaryKey => { - write!(f, "duplicate primary key") - } - ValidationError::UnknownTable { path } => { - write!(f, "unknown table: {path:?}") - } - ValidationError::TableMismatch { expected, actual } => { - write!( - f, - "table mismatch: expected: {expected:?}, actual: {actual:?}" - ) - } - ValidationError::TxnIdMismatch { current, got } => { - write!( - f, - "row handle belongs to a different transaction: current {current:?}, got {got:?}" - ) - } - ValidationError::InvalidRowHandle { reason } => { - write!(f, "invalid row handle: {reason}") - } - } - } + #[error("row handle belongs to a different transaction: current {current:?}, got {got:?}")] + TxnIdMismatch { current: TxnId, got: TxnId }, + #[error("invalid row handle: {reason}")] + InvalidRowHandle { reason: String }, } -impl Error for ValidationError {} - /// One cell in columnar storage: entity id, or primitive. #[derive(Debug, Clone, PartialEq, Eq)] pub enum CellValue { diff --git a/packages/coln-store/src/txn/inner.rs b/packages/coln-store/src/txn/inner.rs index fc22d4d..c127939 100644 --- a/packages/coln-store/src/txn/inner.rs +++ b/packages/coln-store/src/txn/inner.rs @@ -54,7 +54,7 @@ impl TxnInner { store: &Store, table: &ir::Path, values: Vec, - ) -> Result> { + ) -> Result { let t = store.table_at(table).ok_or(ValidationError::UnknownTable { path: table.clone(), })?; @@ -73,7 +73,7 @@ impl TxnInner { store: &Store, table: &ir::Path, values: Vec, - ) -> Result> { + ) -> Result { let txn_values = values .into_iter() .map(|v| v.to_txn_cell_value(self.tx_id)) @@ -91,7 +91,7 @@ impl TxnInner { store: &Store, table: &ir::Path, values: Vec, - ) -> Result> { + ) -> Result { self.add_cell_values(store, table, values) } @@ -107,7 +107,7 @@ impl TxnInner { .for_each(|handle| handle.finalize(h)); } - pub(crate) fn commit(self, store: &mut Store) -> Result> { + pub(crate) fn commit(self, store: &mut Store) -> Result { info!(op_count = self.pending.len(), "commit txn"); let TxnInner { deps, diff --git a/packages/coln-store/src/txn/mod.rs b/packages/coln-store/src/txn/mod.rs index 7c4a238..184d001 100644 --- a/packages/coln-store/src/txn/mod.rs +++ b/packages/coln-store/src/txn/mod.rs @@ -39,7 +39,7 @@ impl<'a> Transaction<'a> { &mut self, table: &ir::Path, values: Vec, - ) -> Result> { + ) -> Result { self.inner.add(self.store, table, values) } @@ -49,11 +49,11 @@ impl<'a> Transaction<'a> { &mut self, table: &ir::Path, values: Vec, - ) -> Result> { + ) -> Result { self.inner.add_internal(self.store, table, values) } - pub fn commit(self) -> Result> { + pub fn commit(self) -> Result { self.inner.commit(self.store) } // pub fn commit_with(mut self, opts: CommitOptions) -> Result { ... } @@ -77,13 +77,13 @@ impl OwnedTransaction { &mut self, table: &ir::Path, values: Vec, - ) -> Result> { + ) -> Result { self.inner.add(&self.store, table, values) } // We need to return Store to the user for roll back purposes, so the Err variant must be large #[allow(clippy::result_large_err)] - pub fn commit(mut self) -> Result<(CommitHash, Store), (Box, Store)> { + pub fn commit(mut self) -> Result<(CommitHash, Store), (StoreIntError, Store)> { match self.inner.commit(&mut self.store) { Ok(hash) => Ok((hash, self.store)), Err(err) => Err((err, self.store)), @@ -148,13 +148,13 @@ mod tests { .add(&Path::from("missing"), vec![1_i64.into()]) .unwrap_err(); assert!(matches!( - *err, + err, StoreIntError::Validation(ValidationError::UnknownTable { .. }) )); let err = tx.add(&path, vec![1_i64.into(), 2_i64.into()]).unwrap_err(); assert!(matches!( - *err, + err, StoreIntError::Validation(ValidationError::ColumnCount { .. }) )); } @@ -170,7 +170,7 @@ mod tests { .expect("add"); let (err, recovered) = tx.commit().unwrap_err(); - assert!(matches!(*err, StoreIntError::Law(_))); + assert!(matches!(err, StoreIntError::Law(_))); assert_eq!(recovered.table_at(&link).expect("Link").row_count(), 0); } @@ -267,13 +267,13 @@ mod tests { .commit() .expect_err("duplicate singleton row should fail"); assert!(matches!( - *err, + err, StoreIntError::Validation(ValidationError::DuplicatePrimaryKey) )); let err = node.row_id().expect_err("failed commit invalidates handle"); assert!(matches!( - *err, + err, StoreIntError::Validation(ValidationError::InvalidRowHandle { .. }) )); @@ -282,7 +282,7 @@ mod tests { .add(&edges, vec![node.into()]) .expect_err("invalid handle cannot be reused"); assert!(matches!( - *err, + err, StoreIntError::Validation(ValidationError::InvalidRowHandle { .. }) )); } diff --git a/packages/coln-store/src/txn/ops.rs b/packages/coln-store/src/txn/ops.rs index 78d585f..df6ad4e 100644 --- a/packages/coln-store/src/txn/ops.rs +++ b/packages/coln-store/src/txn/ops.rs @@ -49,7 +49,7 @@ pub struct RowHandle { } impl RowHandle { - pub fn row_id(&self) -> Result> { + pub fn row_id(&self) -> Result { match &*self.state.borrow() { RowHandleState::Existing(row_id) => Ok(*row_id), RowHandleState::Pending { .. } => Err(ValidationError::InvalidRowHandle { @@ -65,7 +65,7 @@ impl RowHandle { /// For FFI authors only #[doc(hidden)] - pub fn pending_ids(&self) -> Result<(u64, u32), Box> { + pub fn pending_ids(&self) -> Result<(u64, u32), StoreIntError> { match *self.state.borrow() { RowHandleState::Pending { tx_id, counter } => Ok((tx_id.as_u64(), counter)), _ => Err(ValidationError::InvalidRowHandle { @@ -78,7 +78,7 @@ impl RowHandle { pub(crate) fn to_txn_cell_value( &self, current_tx: TxnId, - ) -> Result> { + ) -> Result { match &*self.state.borrow() { RowHandleState::Existing(row_id) => Ok(TxnCellValue::Id(RowRef::Existing(*row_id))), RowHandleState::Pending { tx_id, counter } if *tx_id == current_tx => { @@ -130,7 +130,7 @@ impl TxnValue { pub(crate) fn to_txn_cell_value( &self, current_tx: TxnId, - ) -> Result> { + ) -> Result { match self { TxnValue::Id(handle) => handle.to_txn_cell_value(current_tx), TxnValue::Int(value) => Ok(TxnCellValue::Int(*value)), diff --git a/packages/coln-store/tests/test_path.rs b/packages/coln-store/tests/test_path.rs index 2efbb29..d8b0467 100644 --- a/packages/coln-store/tests/test_path.rs +++ b/packages/coln-store/tests/test_path.rs @@ -40,7 +40,7 @@ fn fixture_theory(name: &str) -> FlatRealm { serde_json::from_str(&json).expect("parse FlatRealm from JSON") } -fn add_basic_data_to_path(store: &mut Store) -> Result<(), Box> { +fn add_basic_data_to_path(store: &mut Store) -> Result<(), StoreIntError> { let graphs = Path::from("Path.Graphs"); let g0 = Path::from("Path.G0"); let g1 = Path::from("Path.G1"); @@ -60,10 +60,7 @@ fn add_basic_data_to_path(store: &mut Store) -> Result<(), Box> { Ok(()) } -fn add_vertex_to_graph( - store: &mut Store, - graph_row: usize, -) -> Result> { +fn add_vertex_to_graph(store: &mut Store, graph_row: usize) -> Result { let graphs = Path::from("Path.Graphs"); let gv = Path::from("Path.G.V"); let graph = store @@ -77,7 +74,7 @@ fn add_vertex_to_graph( tx.commit() } -fn add_extra_edge_to_first_graph(store: &mut Store) -> Result> { +fn add_extra_edge_to_first_graph(store: &mut Store) -> Result { let graphs = Path::from("Path.Graphs"); let gv = Path::from("Path.G.V"); let ge = Path::from("Path.G.E"); @@ -234,7 +231,7 @@ fn test_missing_graph_witness_rejects_batch_without_mutation() { tx.add(&Path::from("Path.Graphs"), vec![]) .expect("add graph row"); let err = tx.commit().expect_err("missing g0 and g1"); - assert!(matches!(*err, StoreIntError::Law(_))); + assert!(matches!(err, StoreIntError::Law(_))); assert_eq!( store @@ -283,7 +280,7 @@ fn test_fk() { .expect("add edge"); let err = tx.commit().expect_err("missing v2"); - assert!(matches!(*err, StoreIntError::Law(_))); + assert!(matches!(err, StoreIntError::Law(_))); } #[test]