#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EntityAction {
Update,
Delete,
Persist,
Recover,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EntityStatus {
New,
Persisted,
PersistedDeleted,
Updated,
UpdatedDeleted,
UpdatedRecover,
Refer,
}
impl Default for EntityStatus {
fn default() -> Self {
EntityStatus::New
}
}
impl EntityStatus {
pub fn next(self, action: EntityAction) -> Result<EntityStatus, String> {
match (self, action) {
(EntityStatus::New, EntityAction::Update) => Ok(EntityStatus::New),
(EntityStatus::New, EntityAction::Persist) => Ok(EntityStatus::Persisted),
(EntityStatus::Persisted, EntityAction::Update) => Ok(EntityStatus::Updated),
(EntityStatus::Persisted, EntityAction::Delete) => Ok(EntityStatus::UpdatedDeleted),
(EntityStatus::PersistedDeleted, EntityAction::Recover) => {
Ok(EntityStatus::UpdatedRecover)
}
(EntityStatus::Updated, EntityAction::Update) => Ok(EntityStatus::Updated),
(EntityStatus::Updated, EntityAction::Persist) => Ok(EntityStatus::Persisted),
(EntityStatus::UpdatedDeleted, EntityAction::Persist) => {
Ok(EntityStatus::PersistedDeleted)
}
(EntityStatus::UpdatedDeleted, EntityAction::Delete) => {
Ok(EntityStatus::UpdatedDeleted)
}
(EntityStatus::UpdatedRecover, EntityAction::Persist) => Ok(EntityStatus::Persisted),
(EntityStatus::UpdatedRecover, EntityAction::Recover) => {
Ok(EntityStatus::UpdatedRecover)
}
(status, action) => Err(format!(
"invalid entity status transition: {:?} + {:?}",
status, action
)),
}
}
pub fn need_persist(&self) -> bool {
matches!(
self,
EntityStatus::New
| EntityStatus::Updated
| EntityStatus::UpdatedDeleted
| EntityStatus::UpdatedRecover
)
}
pub fn is_new(&self) -> bool {
matches!(self, EntityStatus::New)
}
pub fn is_updated(&self) -> bool {
matches!(self, EntityStatus::Updated)
}
pub fn is_deleted(&self) -> bool {
matches!(
self,
EntityStatus::UpdatedDeleted | EntityStatus::PersistedDeleted
)
}
pub fn is_recover(&self) -> bool {
matches!(self, EntityStatus::UpdatedRecover)
}
}
impl std::fmt::Display for EntityStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EntityStatus::New => write!(f, "New"),
EntityStatus::Persisted => write!(f, "Persisted"),
EntityStatus::PersistedDeleted => write!(f, "PersistedDeleted"),
EntityStatus::Updated => write!(f, "Updated"),
EntityStatus::UpdatedDeleted => write!(f, "UpdatedDeleted"),
EntityStatus::UpdatedRecover => write!(f, "UpdatedRecover"),
EntityStatus::Refer => write!(f, "Refer"),
}
}
}
impl std::fmt::Display for EntityAction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EntityAction::Update => write!(f, "Update"),
EntityAction::Delete => write!(f, "Delete"),
EntityAction::Persist => write!(f, "Persist"),
EntityAction::Recover => write!(f, "Recover"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_new() {
assert_eq!(EntityStatus::default(), EntityStatus::New);
}
#[test]
fn new_update_stays_new() {
assert_eq!(
EntityStatus::New.next(EntityAction::Update).unwrap(),
EntityStatus::New
);
}
#[test]
fn new_persist_becomes_persisted() {
assert_eq!(
EntityStatus::New.next(EntityAction::Persist).unwrap(),
EntityStatus::Persisted
);
}
#[test]
fn persisted_update_becomes_updated() {
assert_eq!(
EntityStatus::Persisted.next(EntityAction::Update).unwrap(),
EntityStatus::Updated
);
}
#[test]
fn persisted_delete_becomes_updated_deleted() {
assert_eq!(
EntityStatus::Persisted.next(EntityAction::Delete).unwrap(),
EntityStatus::UpdatedDeleted
);
}
#[test]
fn persisted_deleted_recover_becomes_updated_recover() {
assert_eq!(
EntityStatus::PersistedDeleted
.next(EntityAction::Recover)
.unwrap(),
EntityStatus::UpdatedRecover
);
}
#[test]
fn updated_update_stays_updated() {
assert_eq!(
EntityStatus::Updated.next(EntityAction::Update).unwrap(),
EntityStatus::Updated
);
}
#[test]
fn updated_persist_becomes_persisted() {
assert_eq!(
EntityStatus::Updated.next(EntityAction::Persist).unwrap(),
EntityStatus::Persisted
);
}
#[test]
fn updated_deleted_persist_becomes_persisted_deleted() {
assert_eq!(
EntityStatus::UpdatedDeleted
.next(EntityAction::Persist)
.unwrap(),
EntityStatus::PersistedDeleted
);
}
#[test]
fn updated_deleted_delete_stays_updated_deleted() {
assert_eq!(
EntityStatus::UpdatedDeleted
.next(EntityAction::Delete)
.unwrap(),
EntityStatus::UpdatedDeleted
);
}
#[test]
fn updated_recover_persist_becomes_persisted() {
assert_eq!(
EntityStatus::UpdatedRecover
.next(EntityAction::Persist)
.unwrap(),
EntityStatus::Persisted
);
}
#[test]
fn updated_recover_recover_stays_updated_recover() {
assert_eq!(
EntityStatus::UpdatedRecover
.next(EntityAction::Recover)
.unwrap(),
EntityStatus::UpdatedRecover
);
}
#[test]
fn invalid_transition_returns_err() {
assert!(EntityStatus::New.next(EntityAction::Delete).is_err());
assert!(EntityStatus::New.next(EntityAction::Recover).is_err());
assert!(EntityStatus::Persisted.next(EntityAction::Persist).is_err());
assert!(EntityStatus::Persisted.next(EntityAction::Recover).is_err());
assert!(EntityStatus::PersistedDeleted
.next(EntityAction::Update)
.is_err());
assert!(EntityStatus::PersistedDeleted
.next(EntityAction::Delete)
.is_err());
assert!(EntityStatus::PersistedDeleted
.next(EntityAction::Persist)
.is_err());
assert!(EntityStatus::Updated.next(EntityAction::Delete).is_err());
assert!(EntityStatus::Updated.next(EntityAction::Recover).is_err());
assert!(EntityStatus::UpdatedDeleted
.next(EntityAction::Update)
.is_err());
assert!(EntityStatus::UpdatedDeleted
.next(EntityAction::Recover)
.is_err());
assert!(EntityStatus::UpdatedRecover
.next(EntityAction::Update)
.is_err());
assert!(EntityStatus::UpdatedRecover
.next(EntityAction::Delete)
.is_err());
assert!(EntityStatus::Refer.next(EntityAction::Update).is_err());
assert!(EntityStatus::Refer.next(EntityAction::Delete).is_err());
assert!(EntityStatus::Refer.next(EntityAction::Persist).is_err());
assert!(EntityStatus::Refer.next(EntityAction::Recover).is_err());
}
#[test]
fn need_persist_flags() {
assert!(EntityStatus::New.need_persist());
assert!(!EntityStatus::Persisted.need_persist());
assert!(!EntityStatus::PersistedDeleted.need_persist());
assert!(EntityStatus::Updated.need_persist());
assert!(EntityStatus::UpdatedDeleted.need_persist());
assert!(EntityStatus::UpdatedRecover.need_persist());
assert!(!EntityStatus::Refer.need_persist());
}
#[test]
fn helper_predicates() {
assert!(EntityStatus::New.is_new());
assert!(!EntityStatus::Persisted.is_new());
assert!(EntityStatus::Updated.is_updated());
assert!(!EntityStatus::New.is_updated());
assert!(EntityStatus::UpdatedDeleted.is_deleted());
assert!(EntityStatus::PersistedDeleted.is_deleted());
assert!(!EntityStatus::Updated.is_deleted());
assert!(EntityStatus::UpdatedRecover.is_recover());
assert!(!EntityStatus::Updated.is_recover());
}
#[test]
fn full_lifecycle_create_update_delete() {
let status = EntityStatus::default();
assert_eq!(status, EntityStatus::New);
let status = status.next(EntityAction::Persist).unwrap();
assert_eq!(status, EntityStatus::Persisted);
let status = status.next(EntityAction::Update).unwrap();
assert_eq!(status, EntityStatus::Updated);
let status = status.next(EntityAction::Persist).unwrap();
assert_eq!(status, EntityStatus::Persisted);
let status = status.next(EntityAction::Delete).unwrap();
assert_eq!(status, EntityStatus::UpdatedDeleted);
let status = status.next(EntityAction::Persist).unwrap();
assert_eq!(status, EntityStatus::PersistedDeleted);
let status = status.next(EntityAction::Recover).unwrap();
assert_eq!(status, EntityStatus::UpdatedRecover);
let status = status.next(EntityAction::Persist).unwrap();
assert_eq!(status, EntityStatus::Persisted);
}
}