#[derive(Debug, thiserror::Error)]
pub enum FlozError {
#[error("Record not found")]
NotFound,
#[error("Unique constraint violated: {0}")]
UniqueViolation(String),
#[error("Foreign key constraint violated: {0}")]
ForeignKeyViolation(String),
#[error("Cannot save — entity has no primary key (use create() for new records)")]
UnsavedEntity,
#[error("Bulk insert row {row} is missing column `{column}`")]
MismatchedBulkInsertColumns {
row: usize,
column: String,
},
#[error("DELETE requires .where_() or .all() — refusing to delete all rows without explicit intent")]
UnguardedDelete,
#[error("UPDATE requires .where_() — refusing to update all rows without explicit intent")]
UnguardedUpdate,
#[error("Database error: {0}")]
Database(#[from] sqlx::Error),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("Validation error: {0}")]
ValidationError(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_display_not_found() {
let e = FlozError::NotFound;
assert_eq!(e.to_string(), "Record not found");
}
#[test]
fn error_display_unsaved_entity() {
let e = FlozError::UnsavedEntity;
assert!(e.to_string().contains("create()"));
}
#[test]
fn error_display_mismatched_bulk() {
let e = FlozError::MismatchedBulkInsertColumns {
row: 2,
column: "age".to_string(),
};
assert!(e.to_string().contains("row 2"));
assert!(e.to_string().contains("age"));
}
#[test]
fn error_display_unguarded_delete() {
let e = FlozError::UnguardedDelete;
assert!(e.to_string().contains(".where_()"));
assert!(e.to_string().contains(".all()"));
}
#[test]
fn error_display_unique_violation() {
let e = FlozError::UniqueViolation("users_email_key".into());
assert!(e.to_string().contains("users_email_key"));
}
#[test]
fn error_is_send_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<FlozError>();
assert_sync::<FlozError>();
}
}