#[derive(Debug, thiserror::Error)]
pub enum ForceSyncError {
#[error("database pool error: {0}")]
Pool(#[from] deadpool_postgres::PoolError),
#[error("database error: {0}")]
Database(#[from] tokio_postgres::Error),
#[error("sync journal entries require a source cursor")]
MissingSourceCursor,
#[error(
"transaction callback failed and rollback also failed: callback={callback}; rollback={rollback}"
)]
TransactionRollback {
callback: Box<Self>,
rollback: tokio_postgres::Error,
},
#[error("sync key {part} cannot be empty")]
EmptySyncKeyPart {
part: &'static str,
},
#[error("missing {entity}")]
NotFound {
entity: &'static str,
},
#[error("missing required configuration: {field}")]
MissingConfiguration {
field: &'static str,
},
#[error("lease duration is out of range")]
InvalidLeaseDuration,
#[error("json error: {0}")]
Json(#[from] serde_json::Error),
#[error("pubsub error: {0}")]
PubSub(Box<force_pubsub::PubSubError>),
#[error("invalid outbox operation: {op}")]
InvalidOutboxOperation {
op: String,
},
#[error("invalid outbox cursor: {cursor}")]
InvalidOutboxCursor {
cursor: String,
},
#[error("invalid stored value for {field}: {value}")]
InvalidStoredValue {
field: &'static str,
value: String,
},
#[error("not implemented")]
NotImplemented,
}
impl From<force_pubsub::PubSubError> for ForceSyncError {
fn from(error: force_pubsub::PubSubError) -> Self {
Self::PubSub(Box::new(error))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pubsub_error_conversion() {
let pubsub_err = force_pubsub::PubSubError::Config("test".to_string());
let sync_err: ForceSyncError = pubsub_err.into();
assert!(matches!(sync_err, ForceSyncError::PubSub(_)));
}
#[test]
fn test_pool_error_conversion() {
let pool_err = deadpool_postgres::PoolError::Closed;
let sync_err: ForceSyncError = pool_err.into();
assert!(matches!(sync_err, ForceSyncError::Pool(_)));
}
#[test]
fn test_missing_config_display() {
let err = ForceSyncError::MissingConfiguration { field: "tenant_id" };
assert_eq!(err.to_string(), "missing required configuration: tenant_id");
}
#[test]
fn test_missing_source_cursor_display() {
let err = ForceSyncError::MissingSourceCursor;
assert_eq!(
err.to_string(),
"sync journal entries require a source cursor"
);
}
#[test]
fn test_missing_not_found_display() {
let err = ForceSyncError::NotFound { entity: "Account" };
assert_eq!(err.to_string(), "missing Account");
}
#[test]
fn test_invalid_lease_duration_display() {
let err = ForceSyncError::InvalidLeaseDuration;
assert_eq!(err.to_string(), "lease duration is out of range");
}
#[test]
fn test_not_implemented_display() {
let err = ForceSyncError::NotImplemented;
assert_eq!(err.to_string(), "not implemented");
}
}