diff --git a/src/database/db_connection.rs b/src/database/db_connection.rs index d9095d75..3681c1be 100644 --- a/src/database/db_connection.rs +++ b/src/database/db_connection.rs @@ -36,6 +36,12 @@ pub enum DatabaseConnection { /// The same as a [DatabaseConnection] pub type DbConn = DatabaseConnection; +impl Default for DatabaseConnection { + fn default() -> Self { + Self::Disconnected + } +} + /// The type of database backend for real world databases. /// This is enabled by feature flags as specified in the crate documentation #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -50,6 +56,7 @@ pub enum DatabaseBackend { /// The same as [DatabaseBackend] just shorter :) pub type DbBackend = DatabaseBackend; + #[derive(Debug)] pub(crate) enum InnerConnection { #[cfg(feature = "sqlx-mysql")] @@ -62,12 +69,6 @@ pub(crate) enum InnerConnection { Mock(std::sync::Arc), } -impl Default for DatabaseConnection { - fn default() -> Self { - Self::Disconnected - } -} - impl std::fmt::Debug for DatabaseConnection { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( @@ -116,9 +117,7 @@ impl ConnectionTrait for DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.execute(stmt).await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.execute(stmt), - DatabaseConnection::Disconnected => { - Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned()))) - } + DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -142,9 +141,7 @@ impl ConnectionTrait for DatabaseConnection { let stmt = Statement::from_string(db_backend, sql.into()); conn.execute(stmt) } - DatabaseConnection::Disconnected => { - Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned()))) - } + DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -160,9 +157,7 @@ impl ConnectionTrait for DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_one(stmt).await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.query_one(stmt), - DatabaseConnection::Disconnected => { - Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned()))) - } + DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -178,9 +173,7 @@ impl ConnectionTrait for DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.query_all(stmt).await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => conn.query_all(stmt), - DatabaseConnection::Disconnected => { - Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned()))) - } + DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -195,25 +188,25 @@ impl StreamTrait for DatabaseConnection { type Stream<'a> = crate::QueryStream; #[instrument(level = "trace")] - #[allow(unused_variables, unreachable_code)] + #[allow(unused_variables)] fn stream<'a>( &'a self, stmt: Statement, ) -> Pin, DbErr>> + 'a + Send>> { Box::pin(async move { - Ok(match self { + match self { #[cfg(feature = "sqlx-mysql")] - DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.stream(stmt).await?, + DatabaseConnection::SqlxMySqlPoolConnection(conn) => conn.stream(stmt).await, #[cfg(feature = "sqlx-postgres")] - DatabaseConnection::SqlxPostgresPoolConnection(conn) => conn.stream(stmt).await?, + DatabaseConnection::SqlxPostgresPoolConnection(conn) => conn.stream(stmt).await, #[cfg(feature = "sqlx-sqlite")] - DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.stream(stmt).await?, + DatabaseConnection::SqlxSqlitePoolConnection(conn) => conn.stream(stmt).await, #[cfg(feature = "mock")] DatabaseConnection::MockDatabaseConnection(conn) => { - crate::QueryStream::from((Arc::clone(conn), stmt, None)) + Ok(crate::QueryStream::from((Arc::clone(conn), stmt, None))) } - DatabaseConnection::Disconnected => panic!("Disconnected"), - }) + DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), + } }) } } @@ -233,7 +226,7 @@ impl TransactionTrait for DatabaseConnection { DatabaseConnection::MockDatabaseConnection(conn) => { DatabaseTransaction::new_mock(Arc::clone(conn), None).await } - DatabaseConnection::Disconnected => panic!("Disconnected"), + DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -260,7 +253,7 @@ impl TransactionTrait for DatabaseConnection { DatabaseConnection::MockDatabaseConnection(conn) => { DatabaseTransaction::new_mock(Arc::clone(conn), None).await } - DatabaseConnection::Disconnected => panic!("Disconnected"), + DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } @@ -296,7 +289,7 @@ impl TransactionTrait for DatabaseConnection { .map_err(TransactionError::Connection)?; transaction.run(_callback).await } - DatabaseConnection::Disconnected => panic!("Disconnected"), + DatabaseConnection::Disconnected => Err(conn_err("Disconnected").into()), } } @@ -340,7 +333,7 @@ impl TransactionTrait for DatabaseConnection { .map_err(TransactionError::Connection)?; transaction.run(_callback).await } - DatabaseConnection::Disconnected => panic!("Disconnected"), + DatabaseConnection::Disconnected => Err(conn_err("Disconnected").into()), } } } @@ -348,16 +341,28 @@ impl TransactionTrait for DatabaseConnection { #[cfg(feature = "mock")] impl DatabaseConnection { /// Generate a database connection for testing the Mock database + /// + /// # Panics + /// + /// Panics if [DbConn] is not a mock connection. pub fn as_mock_connection(&self) -> &crate::MockDatabaseConnection { match self { DatabaseConnection::MockDatabaseConnection(mock_conn) => mock_conn, - _ => panic!("not mock connection"), + _ => panic!("Not mock connection"), } } - /// Get the transaction log as a collection Vec<[crate::Transaction]> + /// Get the transaction log as a collection Vec<[crate::Transaction]> + /// + /// # Panics + /// + /// Panics if the mocker mutex is being held by another thread. pub fn into_transaction_log(self) -> Vec { - let mut mocker = self.as_mock_connection().get_mocker_mutex().lock().unwrap(); + let mut mocker = self + .as_mock_connection() + .get_mocker_mutex() + .lock() + .expect("Fail to acquire mocker"); mocker.drain_transaction_log() } } @@ -399,9 +404,7 @@ impl DatabaseConnection { // Nothing to cleanup, we just consume the `DatabaseConnection` Ok(()) } - DatabaseConnection::Disconnected => { - Err(DbErr::Conn(RuntimeErr::Internal("Disconnected".to_owned()))) - } + DatabaseConnection::Disconnected => Err(conn_err("Disconnected")), } } } @@ -409,6 +412,10 @@ impl DatabaseConnection { #[cfg(feature = "sea-orm-internal")] impl DatabaseConnection { /// Get [sqlx::MySqlPool] + /// + /// # Panics + /// + /// Panics if [DbConn] is not a MySQL connection. #[cfg(feature = "sqlx-mysql")] pub fn get_mysql_connection_pool(&self) -> &sqlx::MySqlPool { match self { @@ -418,6 +425,10 @@ impl DatabaseConnection { } /// Get [sqlx::PgPool] + /// + /// # Panics + /// + /// Panics if [DbConn] is not a Postgres connection. #[cfg(feature = "sqlx-postgres")] pub fn get_postgres_connection_pool(&self) -> &sqlx::PgPool { match self { @@ -427,6 +438,10 @@ impl DatabaseConnection { } /// Get [sqlx::SqlitePool] + /// + /// # Panics + /// + /// Panics if [DbConn] is not a SQLite connection. #[cfg(feature = "sqlx-sqlite")] pub fn get_sqlite_connection_pool(&self) -> &sqlx::SqlitePool { match self { @@ -440,7 +455,7 @@ impl DbBackend { /// Check if the URI is the same as the specified database backend. /// Returns true if they match. pub fn is_prefix_of(self, base_url: &str) -> bool { - let base_url_parsed = Url::parse(base_url).unwrap(); + let base_url_parsed = Url::parse(base_url).expect("Fail to parse database URL"); match self { Self::Postgres => { base_url_parsed.scheme() == "postgres" || base_url_parsed.scheme() == "postgresql" diff --git a/src/database/mock.rs b/src/database/mock.rs index 88cea594..5e762b64 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -123,9 +123,7 @@ impl MockDatabaseTrait for MockDatabase { if counter < self.exec_results.len() { match std::mem::replace( &mut self.exec_results[counter], - Err(DbErr::Exec(RuntimeErr::Internal( - "this value has been consumed already".to_owned(), - ))), + Err(exec_err("this value has been consumed already")), ) { Ok(result) => Ok(ExecResult { result: ExecResultHolder::Mock(result), @@ -133,9 +131,7 @@ impl MockDatabaseTrait for MockDatabase { Err(err) => Err(err), } } else { - Err(DbErr::Exec(RuntimeErr::Internal( - "`exec_results` buffer is empty".to_owned(), - ))) + Err(exec_err("`exec_results` buffer is empty")) } } @@ -149,9 +145,7 @@ impl MockDatabaseTrait for MockDatabase { if counter < self.query_results.len() { match std::mem::replace( &mut self.query_results[counter], - Err(DbErr::Query(RuntimeErr::Internal( - "this value has been consumed already".to_owned(), - ))), + Err(query_err("this value has been consumed already")), ) { Ok(result) => Ok(result .into_iter() @@ -162,45 +156,43 @@ impl MockDatabaseTrait for MockDatabase { Err(err) => Err(err), } } else { - Err(DbErr::Query(RuntimeErr::Internal( - "`query_results` buffer is empty.".to_owned(), - ))) + Err(query_err("`query_results` buffer is empty.")) } } #[instrument(level = "trace")] fn begin(&mut self) { - if self.transaction.is_some() { - self.transaction - .as_mut() - .unwrap() - .begin_nested(self.db_backend); - } else { - self.transaction = Some(OpenTransaction::init()); + match self.transaction.as_mut() { + Some(transaction) => transaction.begin_nested(self.db_backend), + None => self.transaction = Some(OpenTransaction::init()), } } #[instrument(level = "trace")] fn commit(&mut self) { - if self.transaction.is_some() { - if self.transaction.as_mut().unwrap().commit(self.db_backend) { - let transaction = self.transaction.take().unwrap(); - self.transaction_log.push(transaction.into_transaction()); + match self.transaction.as_mut() { + Some(transaction) => { + if transaction.commit(self.db_backend) { + if let Some(transaction) = self.transaction.take() { + self.transaction_log.push(transaction.into_transaction()); + } + } } - } else { - panic!("There is no open transaction to commit"); + None => panic!("There is no open transaction to commit"), } } #[instrument(level = "trace")] fn rollback(&mut self) { - if self.transaction.is_some() { - if self.transaction.as_mut().unwrap().rollback(self.db_backend) { - let transaction = self.transaction.take().unwrap(); - self.transaction_log.push(transaction.into_transaction()); + match self.transaction.as_mut() { + Some(transaction) => { + if transaction.rollback(self.db_backend) { + if let Some(transaction) = self.transaction.take() { + self.transaction_log.push(transaction.into_transaction()); + } + } } - } else { - panic!("There is no open transaction to rollback"); + None => panic!("There is no open transaction to rollback"), } } @@ -223,17 +215,17 @@ impl MockRow { T::try_from( self.values .get(index) - .unwrap_or_else(|| panic!("No column for ColIdx {index:?}")) + .ok_or_else(|| query_err(format!("No column for ColIdx {index:?}")))? .clone(), ) - .map_err(|e| DbErr::Type(e.to_string())) + .map_err(type_err) } else if let Some(index) = index.as_usize() { - let (_, value) = self.values.iter().nth(*index).ok_or_else(|| { - DbErr::Query(RuntimeErr::Internal(format!( - "Column at index {index} not found" - ))) - })?; - T::try_from(value.clone()).map_err(|e| DbErr::Type(e.to_string())) + let (_, value) = self + .values + .iter() + .nth(*index) + .ok_or_else(|| query_err(format!("Column at index {index} not found")))?; + T::try_from(value.clone()).map_err(type_err) } else { unreachable!("Missing ColIdx implementation for MockRow"); } @@ -393,10 +385,10 @@ impl OpenTransaction { } fn into_transaction(self) -> Transaction { - if self.transaction_depth != 0 { - panic!("There is uncommitted nested transaction."); + match self.transaction_depth { + 0 => Transaction { stmts: self.stmts }, + _ => panic!("There is uncommitted nested transaction"), } - Transaction { stmts: self.stmts } } } @@ -404,8 +396,8 @@ impl OpenTransaction { #[cfg(feature = "mock")] mod tests { use crate::{ - entity::*, tests_cfg::*, DbBackend, DbErr, IntoMockRow, MockDatabase, RuntimeErr, - Statement, Transaction, TransactionError, TransactionTrait, + entity::*, error::*, tests_cfg::*, DbBackend, DbErr, IntoMockRow, MockDatabase, Statement, + Transaction, TransactionError, TransactionTrait, }; use pretty_assertions::assert_eq; @@ -480,7 +472,7 @@ mod tests { Err(TransactionError::Transaction(err)) => { assert_eq!(err, MyErr("test".to_owned())) } - _ => panic!(), + _ => unreachable!(), } assert_eq!( @@ -846,34 +838,26 @@ mod tests { #[smol_potat::test] async fn test_query_err() { let db = MockDatabase::new(DbBackend::MySql) - .append_query_errors([DbErr::Query(RuntimeErr::Internal( - "this is a mock query error".to_owned(), - ))]) + .append_query_errors([query_err("this is a mock query error")]) .into_connection(); assert_eq!( cake::Entity::find().all(&db).await, - Err(DbErr::Query(RuntimeErr::Internal( - "this is a mock query error".to_owned() - ))) + Err(query_err("this is a mock query error")) ); } #[smol_potat::test] async fn test_exec_err() { let db = MockDatabase::new(DbBackend::MySql) - .append_exec_errors([DbErr::Exec(RuntimeErr::Internal( - "this is a mock exec error".to_owned(), - ))]) + .append_exec_errors([exec_err("this is a mock exec error")]) .into_connection(); let model = cake::ActiveModel::new(); assert_eq!( model.save(&db).await, - Err(DbErr::Exec(RuntimeErr::Internal( - "this is a mock exec error".to_owned() - ))) + Err(exec_err("this is a mock exec error")) ); } } diff --git a/src/database/mod.rs b/src/database/mod.rs index 795789e6..d35411ff 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -18,7 +18,7 @@ pub use stream::*; use tracing::instrument; pub use transaction::*; -use crate::{DbErr, RuntimeErr}; +use crate::error::*; /// Defines a database #[derive(Debug, Default)] @@ -77,10 +77,10 @@ impl Database { if crate::MockDatabaseConnector::accepts(&opt.url) { return crate::MockDatabaseConnector::connect(&opt.url).await; } - Err(DbErr::Conn(RuntimeErr::Internal(format!( + Err(conn_err(format!( "The connection string '{}' has no supporting driver.", opt.url - )))) + ))) } } diff --git a/src/database/stream/query.rs b/src/database/stream/query.rs index 037bea7f..c38dc3b3 100644 --- a/src/database/stream/query.rs +++ b/src/database/stream/query.rs @@ -1,9 +1,10 @@ #![allow(missing_docs, unreachable_code, unused_variables)] -use std::{pin::Pin, task::Poll}; +use tracing::instrument; #[cfg(feature = "mock")] use std::sync::Arc; +use std::{pin::Pin, task::Poll}; use futures::Stream; #[cfg(feature = "sqlx-dep")] @@ -12,11 +13,10 @@ use futures::TryStreamExt; #[cfg(feature = "sqlx-dep")] use sqlx::{pool::PoolConnection, Executor}; -use tracing::instrument; - -use crate::{DbErr, InnerConnection, QueryResult, Statement}; - use super::metric::MetricStream; +#[cfg(feature = "sqlx-dep")] +use crate::driver::*; +use crate::{DbErr, InnerConnection, QueryResult, Statement}; /// Creates a stream from a [QueryResult] #[ouroboros::self_referencing] @@ -130,7 +130,7 @@ impl QueryStream { let stream = c .fetch(query) .map_ok(Into::into) - .map_err(crate::sqlx_error_to_query_err); + .map_err(sqlx_error_to_query_err); let elapsed = _start.map(|s| s.elapsed().unwrap_or_default()); MetricStream::new(_metric_callback, stmt, elapsed, stream) } @@ -141,7 +141,7 @@ impl QueryStream { let stream = c .fetch(query) .map_ok(Into::into) - .map_err(crate::sqlx_error_to_query_err); + .map_err(sqlx_error_to_query_err); let elapsed = _start.map(|s| s.elapsed().unwrap_or_default()); MetricStream::new(_metric_callback, stmt, elapsed, stream) } @@ -152,7 +152,7 @@ impl QueryStream { let stream = c .fetch(query) .map_ok(Into::into) - .map_err(crate::sqlx_error_to_query_err); + .map_err(sqlx_error_to_query_err); let elapsed = _start.map(|s| s.elapsed().unwrap_or_default()); MetricStream::new(_metric_callback, stmt, elapsed, stream) } diff --git a/src/database/stream/transaction.rs b/src/database/stream/transaction.rs index cadb6ea7..d64feaba 100644 --- a/src/database/stream/transaction.rs +++ b/src/database/stream/transaction.rs @@ -1,21 +1,19 @@ #![allow(missing_docs)] use std::{ops::DerefMut, pin::Pin, task::Poll}; +use tracing::instrument; -use futures::Stream; #[cfg(feature = "sqlx-dep")] use futures::TryStreamExt; +use futures::{lock::MutexGuard, Stream}; #[cfg(feature = "sqlx-dep")] use sqlx::Executor; -use futures::lock::MutexGuard; - -use tracing::instrument; - -use crate::{DbErr, InnerConnection, QueryResult, Statement}; - use super::metric::MetricStream; +#[cfg(feature = "sqlx-dep")] +use crate::driver::*; +use crate::{DbErr, InnerConnection, QueryResult, Statement}; /// `TransactionStream` cannot be used in a `transaction` closure as it does not impl `Send`. /// It seems to be a Rust limitation right now, and solution to work around this deemed to be extremely hard. @@ -55,7 +53,7 @@ impl<'a> TransactionStream<'a> { let stream = c .fetch(query) .map_ok(Into::into) - .map_err(crate::sqlx_error_to_query_err); + .map_err(sqlx_error_to_query_err); let elapsed = _start.map(|s| s.elapsed().unwrap_or_default()); MetricStream::new(_metric_callback, stmt, elapsed, stream) } @@ -66,7 +64,7 @@ impl<'a> TransactionStream<'a> { let stream = c .fetch(query) .map_ok(Into::into) - .map_err(crate::sqlx_error_to_query_err); + .map_err(sqlx_error_to_query_err); let elapsed = _start.map(|s| s.elapsed().unwrap_or_default()); MetricStream::new(_metric_callback, stmt, elapsed, stream) } @@ -77,7 +75,7 @@ impl<'a> TransactionStream<'a> { let stream = c .fetch(query) .map_ok(Into::into) - .map_err(crate::sqlx_error_to_query_err); + .map_err(sqlx_error_to_query_err); let elapsed = _start.map(|s| s.elapsed().unwrap_or_default()); MetricStream::new(_metric_callback, stmt, elapsed, stream) } diff --git a/src/database/transaction.rs b/src/database/transaction.rs index db9f82f7..c7f15d41 100644 --- a/src/database/transaction.rs +++ b/src/database/transaction.rs @@ -1,6 +1,7 @@ use crate::{ - debug_print, AccessMode, ConnectionTrait, DbBackend, DbErr, ExecResult, InnerConnection, - IsolationLevel, QueryResult, Statement, StreamTrait, TransactionStream, TransactionTrait, + debug_print, error::*, AccessMode, ConnectionTrait, DbBackend, DbErr, ExecResult, + InnerConnection, IsolationLevel, QueryResult, Statement, StreamTrait, TransactionStream, + TransactionTrait, }; #[cfg(feature = "sqlx-dep")] use crate::{sqlx_error_to_exec_err, sqlx_error_to_query_err}; @@ -95,7 +96,6 @@ impl DatabaseTransaction { } #[instrument(level = "trace", skip(metric_callback))] - #[allow(unreachable_code)] async fn begin( conn: Arc>, backend: DbBackend, @@ -117,7 +117,7 @@ impl DatabaseTransaction { .await?; ::TransactionManager::begin(c) .await - .map_err(sqlx_error_to_query_err)?; + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "sqlx-postgres")] InnerConnection::Postgres(ref mut c) => { @@ -130,7 +130,7 @@ impl DatabaseTransaction { isolation_level, access_mode, ) - .await?; + .await } #[cfg(feature = "sqlx-sqlite")] InnerConnection::Sqlite(ref mut c) => { @@ -139,13 +139,16 @@ impl DatabaseTransaction { .await?; ::TransactionManager::begin(c) .await - .map_err(sqlx_error_to_query_err)?; + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "mock")] InnerConnection::Mock(ref mut c) => { c.begin(); + Ok(()) } - } + #[allow(unreachable_patterns)] + _ => Err(conn_err("Disconnected")), + }?; Ok(res) } @@ -181,25 +184,28 @@ impl DatabaseTransaction { InnerConnection::MySql(ref mut c) => { ::TransactionManager::commit(c) .await - .map_err(sqlx_error_to_query_err)? + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "sqlx-postgres")] InnerConnection::Postgres(ref mut c) => { ::TransactionManager::commit(c) .await - .map_err(sqlx_error_to_query_err)? + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "sqlx-sqlite")] InnerConnection::Sqlite(ref mut c) => { ::TransactionManager::commit(c) .await - .map_err(sqlx_error_to_query_err)? + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "mock")] InnerConnection::Mock(ref mut c) => { c.commit(); + Ok(()) } - } + #[allow(unreachable_patterns)] + _ => Err(conn_err("Disconnected")), + }?; self.open = false; Ok(()) } @@ -213,32 +219,35 @@ impl DatabaseTransaction { InnerConnection::MySql(ref mut c) => { ::TransactionManager::rollback(c) .await - .map_err(sqlx_error_to_query_err)? + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "sqlx-postgres")] InnerConnection::Postgres(ref mut c) => { ::TransactionManager::rollback(c) .await - .map_err(sqlx_error_to_query_err)? + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "sqlx-sqlite")] InnerConnection::Sqlite(ref mut c) => { ::TransactionManager::rollback(c) .await - .map_err(sqlx_error_to_query_err)? + .map_err(sqlx_error_to_query_err) } #[cfg(feature = "mock")] InnerConnection::Mock(ref mut c) => { c.rollback(); + Ok(()) } - } + #[allow(unreachable_patterns)] + _ => Err(conn_err("Disconnected")), + }?; self.open = false; Ok(()) } // the rollback is queued and will be performed on next async operation, like returning the connection to the pool #[instrument(level = "trace")] - fn start_rollback(&mut self) { + fn start_rollback(&mut self) -> Result<(), DbErr> { if self.open { if let Some(mut conn) = self.conn.try_lock() { match &mut *conn { @@ -259,13 +268,14 @@ impl DatabaseTransaction { c.rollback(); } #[allow(unreachable_patterns)] - _ => unreachable!(), + _ => return Err(conn_err("Disconnected")), } } else { //this should never happen - panic!("Dropping a locked Transaction"); + return Err(conn_err("Dropping a locked Transaction")); } } + Ok(()) } #[cfg(feature = "sqlx-dep")] @@ -282,7 +292,7 @@ impl DatabaseTransaction { impl Drop for DatabaseTransaction { fn drop(&mut self) { - self.start_rollback(); + self.start_rollback().expect("Fail to rollback transaction"); } } @@ -326,7 +336,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "mock")] InnerConnection::Mock(conn) => return conn.execute(stmt), #[allow(unreachable_patterns)] - _ => unreachable!(), + _ => Err(conn_err("Disconnected")), } } @@ -358,7 +368,7 @@ impl ConnectionTrait for DatabaseTransaction { conn.execute(stmt) } #[allow(unreachable_patterns)] - _ => unreachable!(), + _ => Err(conn_err("Disconnected")), } } @@ -398,7 +408,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "mock")] InnerConnection::Mock(conn) => return conn.query_one(stmt), #[allow(unreachable_patterns)] - _ => unreachable!(), + _ => Err(conn_err("Disconnected")), } } @@ -444,7 +454,7 @@ impl ConnectionTrait for DatabaseTransaction { #[cfg(feature = "mock")] InnerConnection::Mock(conn) => return conn.query_all(stmt), #[allow(unreachable_patterns)] - _ => unreachable!(), + _ => Err(conn_err("Disconnected")), } } } @@ -564,3 +574,12 @@ where } impl std::error::Error for TransactionError where E: std::error::Error {} + +impl From for TransactionError +where + E: std::error::Error, +{ + fn from(e: DbErr) -> Self { + Self::Connection(e) + } +} diff --git a/src/driver/mock.rs b/src/driver/mock.rs index 6f97e9c6..570b5e53 100644 --- a/src/driver/mock.rs +++ b/src/driver/mock.rs @@ -115,7 +115,10 @@ impl MockDatabaseConnection { /// Get the [DatabaseBackend](crate::DatabaseBackend) being used by the [MockDatabase] pub fn get_database_backend(&self) -> DbBackend { - self.mocker.lock().unwrap().get_database_backend() + self.mocker + .lock() + .expect("Fail to acquire mocker") + .get_database_backend() } /// Execute the SQL statement in the [MockDatabase] @@ -123,7 +126,10 @@ impl MockDatabaseConnection { pub fn execute(&self, statement: Statement) -> Result { debug_print!("{}", statement); let counter = self.execute_counter.fetch_add(1, Ordering::SeqCst); - self.mocker.lock().unwrap().execute(counter, statement) + self.mocker + .lock() + .map_err(exec_err)? + .execute(counter, statement) } /// Return one [QueryResult] if the query was successful @@ -131,7 +137,11 @@ impl MockDatabaseConnection { pub fn query_one(&self, statement: Statement) -> Result, DbErr> { debug_print!("{}", statement); let counter = self.query_counter.fetch_add(1, Ordering::SeqCst); - let result = self.mocker.lock().unwrap().query(counter, statement)?; + let result = self + .mocker + .lock() + .map_err(query_err)? + .query(counter, statement)?; Ok(result.into_iter().next()) } @@ -140,7 +150,10 @@ impl MockDatabaseConnection { pub fn query_all(&self, statement: Statement) -> Result, DbErr> { debug_print!("{}", statement); let counter = self.query_counter.fetch_add(1, Ordering::SeqCst); - self.mocker.lock().unwrap().query(counter, statement) + self.mocker + .lock() + .map_err(query_err)? + .query(counter, statement) } /// Return [QueryResult]s from a multi-query operation @@ -158,18 +171,27 @@ impl MockDatabaseConnection { /// Create a statement block of SQL statements that execute together. #[instrument(level = "trace")] pub fn begin(&self) { - self.mocker.lock().unwrap().begin() + self.mocker + .lock() + .expect("Failed to acquire mocker") + .begin() } /// Commit a transaction atomically to the database #[instrument(level = "trace")] pub fn commit(&self) { - self.mocker.lock().unwrap().commit() + self.mocker + .lock() + .expect("Failed to acquire mocker") + .commit() } /// Roll back a faulty transaction #[instrument(level = "trace")] pub fn rollback(&self) { - self.mocker.lock().unwrap().rollback() + self.mocker + .lock() + .expect("Failed to acquire mocker") + .rollback() } } diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index bdad0b78..8268ee94 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -211,7 +211,7 @@ impl SqlxMySqlPoolConnection { .map_err(|e| TransactionError::Connection(e))?; transaction.run(callback).await } else { - Err(TransactionError::Connection(DbErr::ConnectionAcquire)) + Err(DbErr::ConnectionAcquire.into()) } } diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index f766a9b3..d24ebf2b 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -226,7 +226,7 @@ impl SqlxPostgresPoolConnection { .map_err(|e| TransactionError::Connection(e))?; transaction.run(callback).await } else { - Err(TransactionError::Connection(DbErr::ConnectionAcquire)) + Err(DbErr::ConnectionAcquire.into()) } } diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index dfaaa231..6309208d 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -48,8 +48,8 @@ impl SqlxSqliteConnector { .url .parse::() .map_err(sqlx_error_to_conn_err)?; - if options.sqlcipher_key.is_some() { - opt = opt.pragma("key", options.sqlcipher_key.clone().unwrap()); + if let Some(sqlcipher_key) = &options.sqlcipher_key { + opt = opt.pragma("key", sqlcipher_key.clone()); } use sqlx::ConnectOptions; if !options.sqlx_logging { @@ -218,7 +218,7 @@ impl SqlxSqlitePoolConnection { .map_err(|e| TransactionError::Connection(e))?; transaction.run(callback).await } else { - Err(TransactionError::Connection(DbErr::ConnectionAcquire)) + Err(DbErr::ConnectionAcquire.into()) } } diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 7da1d6c3..786653a9 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -155,7 +155,7 @@ where fn try_get_by(res: &QueryResult, index: I) -> Result { ::try_get_by(res, index)? .into_iter() - .map(|value| T::try_from_value(&value).map_err(TryGetError::DbErr)) + .map(|value| T::try_from_value(&value).map_err(Into::into)) .collect() } } @@ -174,7 +174,7 @@ where #[cfg(test)] mod tests { use crate as sea_orm; - use crate::{entity::prelude::*, sea_query::SeaRc, *}; + use crate::{error::*, sea_query::SeaRc, *}; use pretty_assertions::assert_eq; #[test] @@ -210,9 +210,7 @@ mod tests { match v.as_ref() { "B" => Ok(Self::Big), "S" => Ok(Self::Small), - _ => Err(DbErr::Type(format!( - "unexpected value for Category enum: {v}" - ))), + _ => Err(type_err(format!("unexpected value for Category enum: {v}"))), } } @@ -241,9 +239,7 @@ mod tests { assert_eq!( Category::try_from_value(&"A".to_owned()).err(), - Some(DbErr::Type( - "unexpected value for Category enum: A".to_owned() - )) + Some(type_err("unexpected value for Category enum: A")) ); assert_eq!( Category::try_from_value(&"B".to_owned()).ok(), @@ -255,9 +251,7 @@ mod tests { ); assert_eq!( DeriveCategory::try_from_value(&"A".to_owned()).err(), - Some(DbErr::Type( - "unexpected value for DeriveCategory enum: A".to_owned() - )) + Some(type_err("unexpected value for DeriveCategory enum: A")) ); assert_eq!( DeriveCategory::try_from_value(&"B".to_owned()).ok(), @@ -326,7 +320,7 @@ mod tests { assert_eq!($ident::try_from_value(&-10).ok(), Some($ident::Negative)); assert_eq!( $ident::try_from_value(&2).err(), - Some(DbErr::Type(format!( + Some(type_err(format!( "unexpected value for {} enum: 2", stringify!($ident) ))) @@ -391,7 +385,7 @@ mod tests { assert_eq!($ident::try_from_value(&0).ok(), Some($ident::Small)); assert_eq!( $ident::try_from_value(&2).err(), - Some(DbErr::Type(format!( + Some(type_err(format!( "unexpected value for {} enum: 2", stringify!($ident) ))) diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index c15e24f1..b056d731 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -119,6 +119,10 @@ pub trait ActiveModelTrait: Clone + Debug { } /// Get the primary key of the ActiveModel + /// + /// # Panics + /// + /// Panics if arity of primary key exceed maximum arity of [ValueTuple] #[allow(clippy::question_mark)] fn get_primary_key_value(&self) -> Option { let mut cols = ::PrimaryKey::iter(); @@ -547,15 +551,18 @@ pub trait ActiveModelTrait: Clone + Debug { // Convert JSON object into ActiveModel via Model let model: ::Model = - serde_json::from_value(json).map_err(|e| DbErr::Json(e.to_string()))?; + serde_json::from_value(json).map_err(json_err)?; let mut am = model.into_active_model(); // Transform attribute that exists in JSON object into ActiveValue::Set, otherwise ActiveValue::NotSet for (col, json_key_exists) in json_keys { - if json_key_exists && !am.is_not_set(col) { - am.set(col, am.get(col).unwrap()); - } else { - am.not_set(col); + match (json_key_exists, am.get(col)) { + (true, ActiveValue::Set(value) | ActiveValue::Unchanged(value)) => { + am.set(col, value); + } + _ => { + am.not_set(col); + } } } @@ -823,6 +830,10 @@ where } /// Get an owned value of the [ActiveValue] + /// + /// # Panics + /// + /// Panics if it is [ActiveValue::NotSet] pub fn unwrap(self) -> V { match self { ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value, @@ -861,6 +872,9 @@ impl std::convert::AsRef for ActiveValue where V: Into, { + /// # Panics + /// + /// Panics if it is [ActiveValue::NotSet] fn as_ref(&self) -> &V { match self { ActiveValue::Set(value) | ActiveValue::Unchanged(value) => value, diff --git a/src/entity/base_entity.rs b/src/entity/base_entity.rs index af763b9c..02917749 100644 --- a/src/entity/base_entity.rs +++ b/src/entity/base_entity.rs @@ -256,6 +256,10 @@ pub trait EntityTrait: EntityName { /// # Ok(()) /// # } /// ``` + /// + /// # Panics + /// + /// Panics if arity of input values don't match arity of primary key fn find_by_id(values: T) -> Select where T: Into<::ValueType>, @@ -818,6 +822,10 @@ pub trait EntityTrait: EntityName { /// # Ok(()) /// # } /// ``` + /// + /// # Panics + /// + /// Panics if arity of input values don't match arity of primary key fn delete_by_id(values: T) -> DeleteMany where T: Into<::ValueType>, diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 6b413bdf..54a289b4 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -88,9 +88,10 @@ /// // Create a Relation for the Entity /// impl RelationTrait for Relation { /// fn def(&self) -> RelationDef { -/// panic!() +/// unimplemented!() /// } /// } +/// /// // Implement user defined operations for CREATE, UPDATE and DELETE operations /// // to create an ActiveModel using the [ActiveModelBehavior] /// impl ActiveModelBehavior for ActiveModel {} diff --git a/src/entity/relation.rs b/src/entity/relation.rs index 74e49305..ad291f0f 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -303,8 +303,8 @@ where rel_type: b.rel_type, from_tbl: b.from_tbl, to_tbl: b.to_tbl, - from_col: b.from_col.unwrap(), - to_col: b.to_col.unwrap(), + from_col: b.from_col.expect("Reference column is not set"), + to_col: b.to_col.expect("Owner column is not set"), is_owner: b.is_owner, on_delete: b.on_delete, on_update: b.on_update, diff --git a/src/error.rs b/src/error.rs index 9656b125..e470bb6a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -99,3 +99,43 @@ impl Eq for DbErr {} #[derive(Error, Debug)] #[error("Failed to match \"{0}\" as Column")] pub struct ColumnFromStrErr(pub String); + +#[allow(dead_code)] +pub(crate) fn conn_err(s: T) -> DbErr +where + T: ToString, +{ + DbErr::Conn(RuntimeErr::Internal(s.to_string())) +} + +#[allow(dead_code)] +pub(crate) fn exec_err(s: T) -> DbErr +where + T: ToString, +{ + DbErr::Exec(RuntimeErr::Internal(s.to_string())) +} + +#[allow(dead_code)] +pub(crate) fn query_err(s: T) -> DbErr +where + T: ToString, +{ + DbErr::Query(RuntimeErr::Internal(s.to_string())) +} + +#[allow(dead_code)] +pub(crate) fn type_err(s: T) -> DbErr +where + T: ToString, +{ + DbErr::Type(s.to_string()) +} + +#[allow(dead_code)] +pub(crate) fn json_err(s: T) -> DbErr +where + T: ToString, +{ + DbErr::Json(s.to_string()) +} diff --git a/src/executor/execute.rs b/src/executor/execute.rs index f3a7150d..487ca41a 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -28,6 +28,10 @@ pub(crate) enum ExecResultHolder { impl ExecResult { /// Get the last id after `AUTOINCREMENT` is done on the primary key + /// + /// # Panics + /// + /// Postgres does not support retrieving last insert id this way except through `RETURNING` clause pub fn last_insert_id(&self) -> u64 { match &self.result { #[cfg(feature = "sqlx-mysql")] @@ -40,7 +44,7 @@ impl ExecResult { ExecResultHolder::SqlxSqlite(result) => { let last_insert_rowid = result.last_insert_rowid(); if last_insert_rowid < 0 { - panic!("negative last_insert_rowid") + unreachable!("negative last_insert_rowid") } else { last_insert_rowid as u64 } diff --git a/src/executor/insert.rs b/src/executor/insert.rs index b80424f3..96ad56cc 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -126,7 +126,6 @@ where } } -#[allow(unused_variables, unreachable_code)] async fn exec_insert( primary_key: Option, statement: Statement, diff --git a/src/executor/query.rs b/src/executor/query.rs index 87eabd73..6f457306 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -1,7 +1,13 @@ +use crate::{error::*, SelectGetableValue, SelectorRaw, Statement}; +use std::fmt; + #[cfg(feature = "mock")] use crate::debug_print; -use crate::{DbErr, SelectGetableValue, SelectorRaw, Statement}; -use std::fmt; + +#[cfg(feature = "sqlx-dep")] +use crate::driver::*; +#[cfg(feature = "sqlx-dep")] +use sqlx::Row; /// Defines the result of a query operation on a Model #[derive(Debug)] @@ -52,12 +58,18 @@ impl From for DbErr { match e { TryGetError::DbErr(e) => e, TryGetError::Null(s) => { - DbErr::Type(format!("A null value was encountered while decoding {s}")) + type_err(format!("A null value was encountered while decoding {s}")) } } } } +impl From for TryGetError { + fn from(e: DbErr) -> TryGetError { + Self::DbErr(e) + } +} + // QueryResult // impl QueryResult { @@ -236,26 +248,20 @@ macro_rules! try_getable_all { fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_mysql_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxMySql(row) => row + .try_get::, _>(idx.as_sqlx_mysql_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_postgres_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxPostgres(row) => row + .try_get::, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_sqlite_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxSqlite(row) => row + .try_get::, _>(idx.as_sqlx_sqlite_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "mock")] QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); @@ -276,23 +282,21 @@ macro_rules! try_getable_unsigned { fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_mysql_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxMySql(row) => row + .try_get::, _>(idx.as_sqlx_mysql_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(_) => { - panic!("{} unsupported by sqlx-postgres", stringify!($type)) - } + QueryResultRow::SqlxPostgres(_) => Err(type_err(format!( + "{} unsupported by sqlx-postgres", + stringify!($type) + )) + .into()), #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_sqlite_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxSqlite(row) => row + .try_get::, _>(idx.as_sqlx_sqlite_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "mock")] QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); @@ -313,20 +317,22 @@ macro_rules! try_getable_mysql { fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_mysql_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxMySql(row) => row + .try_get::, _>(idx.as_sqlx_mysql_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(_) => { - panic!("{} unsupported by sqlx-postgres", stringify!($type)) - } + QueryResultRow::SqlxPostgres(_) => Err(type_err(format!( + "{} unsupported by sqlx-postgres", + stringify!($type) + )) + .into()), #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(_) => { - panic!("{} unsupported by sqlx-sqlite", stringify!($type)) - } + QueryResultRow::SqlxSqlite(_) => Err(type_err(format!( + "{} unsupported by sqlx-sqlite", + stringify!($type) + )) + .into()), #[cfg(feature = "mock")] QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { debug_print!("{:#?}", e.to_string()); @@ -350,25 +356,21 @@ macro_rules! try_getable_date_time { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use chrono::{DateTime, Utc}; - use sqlx::Row; row.try_get::>, _>(idx.as_sqlx_mysql_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .map_err(|e| sqlx_error_to_query_err(e).into()) .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) .map(|v| v.into()) } #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_postgres_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxPostgres(row) => row + .try_get::, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use chrono::{DateTime, Utc}; - use sqlx::Row; row.try_get::>, _>(idx.as_sqlx_sqlite_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .map_err(|e| sqlx_error_to_query_err(e).into()) .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) .map(|v| v.into()) } @@ -440,32 +442,28 @@ impl TryGetable for Decimal { fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_mysql_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxMySql(row) => row + .try_get::, _>(idx.as_sqlx_mysql_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_postgres_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxPostgres(row) => row + .try_get::, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; let val: Option = row .try_get(idx.as_sqlx_sqlite_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; + .map_err(sqlx_error_to_query_err)?; match val { Some(v) => Decimal::try_from(v).map_err(|e| { - TryGetError::DbErr(DbErr::TryIntoErr { + DbErr::TryIntoErr { from: "f64", into: "Decimal", source: Box::new(e), - }) + } + .into() }), None => Err(err_null_idx_col(idx)), } @@ -491,32 +489,28 @@ impl TryGetable for BigDecimal { fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_mysql_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxMySql(row) => row + .try_get::, _>(idx.as_sqlx_mysql_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_postgres_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxPostgres(row) => row + .try_get::, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; let val: Option = row .try_get(idx.as_sqlx_sqlite_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))?; + .map_err(sqlx_error_to_query_err)?; match val { Some(v) => BigDecimal::try_from(v).map_err(|e| { - TryGetError::DbErr(DbErr::TryIntoErr { + DbErr::TryIntoErr { from: "f64", into: "BigDecimal", source: Box::new(e), - }) + } + .into() }), None => Err(err_null_idx_col(idx)), } @@ -541,26 +535,20 @@ macro_rules! try_getable_uuid { fn try_get_by(res: &QueryResult, idx: I) -> Result { let res: Result = match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_mysql_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxMySql(row) => row + .try_get::, _>(idx.as_sqlx_mysql_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_postgres_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxPostgres(row) => row + .try_get::, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_sqlite_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxSqlite(row) => row + .try_get::, _>(idx.as_sqlx_sqlite_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "mock")] #[allow(unused_variables)] QueryResultRow::Mock(row) => row.try_get::(idx).map_err(|e| { @@ -596,30 +584,25 @@ impl TryGetable for u32 { fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_mysql_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxMySql(row) => row + .try_get::, _>(idx.as_sqlx_mysql_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::postgres::types::Oid; // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`; // Instead, `u32` was wrapped by a `sqlx::Oid`. - use sqlx::Row; row.try_get::, _>(idx.as_sqlx_postgres_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .map_err(|e| sqlx_error_to_query_err(e).into()) .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) .map(|oid| oid.0) } #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - row.try_get::, _>(idx.as_sqlx_sqlite_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxSqlite(row) => row + .try_get::, _>(idx.as_sqlx_sqlite_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "mock")] #[allow(unused_variables)] QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { @@ -649,20 +632,22 @@ mod postgres_array { fn try_get_by(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(_) => { - panic!("{} unsupported by sqlx-mysql", stringify!($type)) - } + QueryResultRow::SqlxMySql(_) => Err(type_err(format!( + "{} unsupported by sqlx-mysql", + stringify!($type) + )) + .into()), #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::>, _>(idx.as_sqlx_postgres_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxPostgres(row) => row + .try_get::>, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(_) => { - panic!("{} unsupported by sqlx-sqlite", stringify!($type)) - } + QueryResultRow::SqlxSqlite(_) => Err(type_err(format!( + "{} unsupported by sqlx-sqlite", + stringify!($type) + )) + .into()), #[cfg(feature = "mock")] #[allow(unused_variables)] QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { @@ -733,20 +718,22 @@ mod postgres_array { fn try_get_by(res: &QueryResult, idx: I) -> Result { let res: Result, TryGetError> = match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - panic!("{} unsupported by sqlx-mysql", stringify!($type)) - } + QueryResultRow::SqlxMySql(_) => Err(type_err(format!( + "{} unsupported by sqlx-mysql", + stringify!($type) + )) + .into()), #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::>, _>(idx.as_sqlx_postgres_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) - } + QueryResultRow::SqlxPostgres(row) => row + .try_get::>, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))), #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(_) => { - panic!("{} unsupported by sqlx-sqlite", stringify!($type)) - } + QueryResultRow::SqlxSqlite(_) => Err(type_err(format!( + "{} unsupported by sqlx-sqlite", + stringify!($type) + )) + .into()), #[cfg(feature = "mock")] QueryResultRow::Mock(row) => { row.try_get::, _>(idx).map_err(|e| { @@ -784,23 +771,24 @@ mod postgres_array { match &res.row { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(_) => { - panic!("{} unsupported by sqlx-mysql", stringify!($type)) + Err(type_err(format!("{} unsupported by sqlx-mysql", stringify!($type))).into()) } #[cfg(feature = "sqlx-postgres")] QueryResultRow::SqlxPostgres(row) => { use sqlx::postgres::types::Oid; // Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`; // Instead, `u32` was wrapped by a `sqlx::Oid`. - use sqlx::Row; row.try_get::>, _>(idx.as_sqlx_postgres_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) + .map_err(|e| sqlx_error_to_query_err(e).into()) .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx))) .map(|oids| oids.into_iter().map(|oid| oid.0).collect()) } #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(_) => { - panic!("{} unsupported by sqlx-sqlite", stringify!($type)) - } + QueryResultRow::SqlxSqlite(_) => Err(type_err(format!( + "{} unsupported by sqlx-sqlite", + stringify!($type) + )) + .into()), #[cfg(feature = "mock")] #[allow(unused_variables)] QueryResultRow::Mock(row) => row.try_get(idx).map_err(|e| { @@ -1050,11 +1038,12 @@ where fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), TryGetError> { if cols.len() < len { - Err(TryGetError::DbErr(DbErr::Type(format!( + Err(type_err(format!( "Expect {} column names supplied but got slice of length {}", len, cols.len() - )))) + )) + .into()) } else { Ok(()) } @@ -1073,26 +1062,20 @@ where fn try_get_from_json(res: &QueryResult, idx: I) -> Result { match &res.row { #[cfg(feature = "sqlx-mysql")] - QueryResultRow::SqlxMySql(row) => { - use sqlx::Row; - row.try_get::>, _>(idx.as_sqlx_mysql_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)) - } + QueryResultRow::SqlxMySql(row) => row + .try_get::>, _>(idx.as_sqlx_mysql_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)), #[cfg(feature = "sqlx-postgres")] - QueryResultRow::SqlxPostgres(row) => { - use sqlx::Row; - row.try_get::>, _>(idx.as_sqlx_postgres_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)) - } + QueryResultRow::SqlxPostgres(row) => row + .try_get::>, _>(idx.as_sqlx_postgres_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)), #[cfg(feature = "sqlx-sqlite")] - QueryResultRow::SqlxSqlite(row) => { - use sqlx::Row; - row.try_get::>, _>(idx.as_sqlx_sqlite_index()) - .map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e))) - .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)) - } + QueryResultRow::SqlxSqlite(row) => row + .try_get::>, _>(idx.as_sqlx_sqlite_index()) + .map_err(|e| sqlx_error_to_query_err(e).into()) + .and_then(|opt| opt.ok_or_else(|| err_null_idx_col(idx)).map(|json| json.0)), #[cfg(feature = "mock")] QueryResultRow::Mock(row) => row .try_get::(idx) @@ -1100,10 +1083,7 @@ where debug_print!("{:#?}", e.to_string()); err_null_idx_col(idx) }) - .and_then(|json| { - serde_json::from_value(json) - .map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string()))) - }), + .and_then(|json| serde_json::from_value(json).map_err(|e| json_err(e).into())), #[allow(unreachable_patterns)] _ => unreachable!(), } diff --git a/src/executor/select.rs b/src/executor/select.rs index c36753f1..c3bb1f29 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -906,11 +906,11 @@ where } } } - if r.is_some() { - acc.push((l, vec![r.unwrap()])); - } else { - acc.push((l, vec![])); - } + let rows = match r { + Some(r) => vec![r], + None => vec![], + }; + acc.push((l, rows)); } acc } diff --git a/src/lib.rs b/src/lib.rs index ca88e4ca..510c5f4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ #![warn(missing_docs)] #![deny( missing_debug_implementations, + clippy::missing_panics_doc, + clippy::unwrap_used, clippy::print_stderr, clippy::print_stdout )] diff --git a/src/query/delete.rs b/src/query/delete.rs index f85255df..73cd4302 100644 --- a/src/query/delete.rs +++ b/src/query/delete.rs @@ -1,6 +1,6 @@ use crate::{ - ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, Iterable, PrimaryKeyToColumn, - QueryFilter, QueryTrait, + ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait, IntoActiveModel, Iterable, + PrimaryKeyToColumn, QueryFilter, QueryTrait, }; use core::marker::PhantomData; use sea_query::DeleteStatement; @@ -109,10 +109,11 @@ where for key in ::PrimaryKey::iter() { let col = key.into_column(); let av = self.model.get(col); - if av.is_set() || av.is_unchanged() { - self = self.filter(col.eq(av.unwrap())); - } else { - panic!("PrimaryKey is not set"); + match av { + ActiveValue::Set(value) | ActiveValue::Unchanged(value) => { + self = self.filter(col.eq(value)); + } + ActiveValue::NotSet => panic!("PrimaryKey is not set"), } } self diff --git a/src/query/insert.rs b/src/query/insert.rs index c7cf0e4b..64dc248b 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -1,5 +1,5 @@ use crate::{ - ActiveModelTrait, ColumnTrait, EntityName, EntityTrait, IntoActiveModel, Iterable, + ActiveModelTrait, ActiveValue, ColumnTrait, EntityName, EntityTrait, IntoActiveModel, Iterable, PrimaryKeyTrait, QueryTrait, }; use core::marker::PhantomData; @@ -109,6 +109,10 @@ where } /// Add a Model to Self + /// + /// # Panics + /// + /// Panics if the column value has discrepancy across rows #[allow(clippy::should_implement_trait)] pub fn add(mut self, m: M) -> Self where @@ -132,9 +136,12 @@ where } else if self.columns[idx] != av_has_val { panic!("columns mismatch"); } - if av_has_val { - columns.push(col); - values.push(col.save_as(Expr::val(av.into_value().unwrap()))); + match av { + ActiveValue::Set(value) | ActiveValue::Unchanged(value) => { + columns.push(col); + values.push(col.save_as(Expr::val(value))); + } + ActiveValue::NotSet => {} } } self.query.columns(columns); diff --git a/src/query/json.rs b/src/query/json.rs index 689fedbd..066f69c2 100644 --- a/src/query/json.rs +++ b/src/query/json.rs @@ -1,4 +1,4 @@ -use crate::{DbErr, FromQueryResult, QueryResult}; +use crate::{error::*, FromQueryResult, QueryResult}; use serde_json::Map; pub use serde_json::Value as JsonValue; diff --git a/src/query/loader.rs b/src/query/loader.rs index b77e45b1..5d9dd37d 100644 --- a/src/query/loader.rs +++ b/src/query/loader.rs @@ -1,6 +1,6 @@ use crate::{ - ColumnTrait, Condition, ConnectionTrait, DbErr, EntityTrait, Identity, ModelTrait, QueryFilter, - Related, RelationType, Select, + error::*, ColumnTrait, Condition, ConnectionTrait, DbErr, EntityTrait, Identity, ModelTrait, + QueryFilter, Related, RelationType, Select, }; use async_trait::async_trait; use sea_query::{Expr, IntoColumnRef, SimpleExpr, ValueTuple}; @@ -76,9 +76,7 @@ where // we verify that is has_one relation match (rel_def).rel_type { RelationType::HasOne => (), - RelationType::HasMany => { - return Err(DbErr::Type("Relation is HasMany instead of HasOne".into())) - } + RelationType::HasMany => return Err(type_err("Relation is HasMany instead of HasOne")), } let keys: Vec = self @@ -126,9 +124,7 @@ where // we verify that is has_many relation match (rel_def).rel_type { RelationType::HasMany => (), - RelationType::HasOne => { - return Err(DbErr::Type("Relation is HasOne instead of HasMany".into())) - } + RelationType::HasOne => return Err(type_err("Relation is HasOne instead of HasMany")), } let keys: Vec = self diff --git a/src/query/update.rs b/src/query/update.rs index b98d93b9..9a1c60c9 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -1,6 +1,6 @@ use crate::{ - ActiveModelTrait, ColumnTrait, EntityTrait, Iterable, PrimaryKeyToColumn, QueryFilter, - QueryTrait, + ActiveModelTrait, ActiveValue, ColumnTrait, EntityTrait, Iterable, PrimaryKeyToColumn, + QueryFilter, QueryTrait, }; use core::marker::PhantomData; use sea_query::{Expr, IntoIden, SimpleExpr, UpdateStatement}; @@ -92,11 +92,11 @@ where fn prepare_filters(mut self) -> Self { for key in ::PrimaryKey::iter() { let col = key.into_column(); - let av = self.model.get(col); - if av.is_set() || av.is_unchanged() { - self = self.filter(col.eq(av.unwrap())); - } else { - panic!("PrimaryKey is not set"); + match self.model.get(col) { + ActiveValue::Set(value) | ActiveValue::Unchanged(value) => { + self = self.filter(col.eq(value)); + } + ActiveValue::NotSet => panic!("PrimaryKey is not set"), } } self @@ -107,10 +107,12 @@ where if ::PrimaryKey::from_column(col).is_some() { continue; } - let av = self.model.get(col); - if av.is_set() { - let expr = col.save_as(Expr::val(av.into_value().unwrap())); - self.query.value(col, expr); + match self.model.get(col) { + ActiveValue::Set(value) => { + let expr = col.save_as(Expr::val(value)); + self.query.value(col, expr); + } + ActiveValue::Unchanged(_) | ActiveValue::NotSet => {} } } self @@ -187,10 +189,12 @@ where A: ActiveModelTrait, { for col in E::Column::iter() { - let av = model.get(col); - if av.is_set() { - let expr = col.save_as(Expr::val(av.into_value().unwrap())); - self.query.value(col, expr); + match model.get(col) { + ActiveValue::Set(value) => { + let expr = col.save_as(Expr::val(value)); + self.query.value(col, expr); + } + ActiveValue::Unchanged(_) | ActiveValue::NotSet => {} } } self diff --git a/src/schema/entity.rs b/src/schema/entity.rs index d65a2064..7aaac164 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -42,6 +42,7 @@ impl Schema { } /// Creates a column definition for example to update a table. + /// /// ``` /// use crate::sea_orm::IdenStatic; /// use sea_orm::{ @@ -61,6 +62,7 @@ impl Schema { /// /// #[derive(Copy, Clone, Debug, EnumIter)] /// pub enum Relation {} + /// /// impl RelationTrait for Relation { /// fn def(&self) -> RelationDef { /// panic!("No RelationDef") diff --git a/src/tests_cfg/indexes.rs b/src/tests_cfg/indexes.rs index e4c2993c..384ac8e6 100644 --- a/src/tests_cfg/indexes.rs +++ b/src/tests_cfg/indexes.rs @@ -44,7 +44,7 @@ impl PrimaryKeyTrait for PrimaryKey { } } -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} impl ColumnTrait for Column { @@ -60,10 +60,4 @@ impl ColumnTrait for Column { } } -impl RelationTrait for Relation { - fn def(&self) -> RelationDef { - panic!() - } -} - impl ActiveModelBehavior for ActiveModel {} diff --git a/src/tests_cfg/rust_keyword.rs b/src/tests_cfg/rust_keyword.rs index 0d5dc474..bdf88c1a 100644 --- a/src/tests_cfg/rust_keyword.rs +++ b/src/tests_cfg/rust_keyword.rs @@ -62,15 +62,9 @@ pub struct Model { pub r#yield: i32, } -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} -impl RelationTrait for Relation { - fn def(&self) -> RelationDef { - panic!("No RelationDef") - } -} - impl ActiveModelBehavior for ActiveModel {} #[cfg(test)] diff --git a/src/tests_cfg/vendor.rs b/src/tests_cfg/vendor.rs index 11e7c057..62aa716c 100644 --- a/src/tests_cfg/vendor.rs +++ b/src/tests_cfg/vendor.rs @@ -9,15 +9,9 @@ pub struct Model { pub name: String, } -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} -impl RelationTrait for Relation { - fn def(&self) -> RelationDef { - panic!() - } -} - impl Related for Entity { fn to() -> RelationDef { super::filling::Relation::Vendor.def()