use joerl::{ActorSystem, ExitReason, gen_statem};
use std::sync::Arc;
#[gen_statem(fsm = r#"
[*] --> draft
draft --> |submit| review
review --> |approve| approved
review --> |reject| rejected
rejected --> |revise| draft
approved --> |publish| published
published --> |archive| [*]
"#)]
#[derive(Debug, Clone)]
struct Document {
title: String,
version: u32,
approver: Option<String>,
revision_count: u32,
}
impl Document {
fn on_transition(
&mut self,
event: DocumentEvent,
state: DocumentState,
) -> DocumentTransitionResult {
match (state.clone(), event.clone()) {
(DocumentState::Draft, DocumentEvent::Submit) => {
println!("📝 Submitting document '{}' for review", self.title);
self.version += 1;
DocumentTransitionResult::Next(DocumentState::Review, self.clone())
}
(DocumentState::Review, DocumentEvent::Approve) => {
println!("✅ Document approved");
self.approver = Some("Reviewer".to_string());
DocumentTransitionResult::Next(DocumentState::Approved, self.clone())
}
(DocumentState::Review, DocumentEvent::Reject) => {
println!("❌ Document rejected - needs changes");
self.revision_count += 1;
DocumentTransitionResult::Next(DocumentState::Rejected, self.clone())
}
(DocumentState::Rejected, DocumentEvent::Revise) => {
println!("🔧 Revising document (revision #{})", self.revision_count);
DocumentTransitionResult::Next(DocumentState::Draft, self.clone())
}
(DocumentState::Approved, DocumentEvent::Publish) => {
println!("🚀 Publishing document to production");
DocumentTransitionResult::Next(DocumentState::Published, self.clone())
}
(DocumentState::Published, DocumentEvent::Archive) => {
println!("📦 Archiving document");
DocumentTransitionResult::Keep(self.clone())
}
_ => {
println!("⚠️ Invalid transition: {:?} -> {:?}", state, event);
DocumentTransitionResult::Error(format!(
"Cannot {:?} from state {:?}",
event, state
))
}
}
}
fn on_enter(&self, old_state: &DocumentState, new_state: &DocumentState, _data: &DocumentData) {
println!(
" 🔄 State: {:?} → {:?} (v{})",
old_state, new_state, self.version
);
}
fn on_terminate(&self, reason: &ExitReason, state: &DocumentState, data: &DocumentData) {
println!("\n═══════════════════════════════════");
println!("📊 Workflow Statistics");
println!("═══════════════════════════════════");
println!(" Document: {}", data.title);
println!(" Final Version: {}", data.version);
println!(" Revisions: {}", data.revision_count);
println!(" Approver: {}", data.approver.as_deref().unwrap_or("None"));
println!(" Final State: {:?}", state);
println!(" Exit Reason: {:?}", reason);
println!("═══════════════════════════════════\n");
}
}
#[tokio::main]
async fn main() {
println!("\n╔══════════════════════════════════════════╗");
println!("║ Document Workflow FSM (gen_statem) ║");
println!("╚══════════════════════════════════════════╝\n");
println!("States: draft → review → approved → published");
println!("Reject path: review → rejected → draft\n");
println!("──────────────────────────────────────────\n");
let system = Arc::new(ActorSystem::new());
let initial_data = Document {
title: "Quarterly Report Q4 2024".to_string(),
version: 0,
approver: None,
revision_count: 0,
};
let doc = Document(&system, initial_data);
println!("📄 Starting workflow for: Quarterly Report Q4 2024\n");
println!("[1] Submit for review");
doc.send(Box::new(DocumentEvent::Submit)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
println!();
println!("[2] Reviewer rejects document");
doc.send(Box::new(DocumentEvent::Reject)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
println!();
println!("[3] Author revises document");
doc.send(Box::new(DocumentEvent::Revise)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
println!();
println!("[4] Resubmit for review");
doc.send(Box::new(DocumentEvent::Submit)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
println!();
println!("[5] Reviewer approves document");
doc.send(Box::new(DocumentEvent::Approve)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
println!();
println!("[6] Publish to production");
doc.send(Box::new(DocumentEvent::Publish)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
println!();
println!("[7] Archive document");
doc.send(Box::new(DocumentEvent::Archive)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
println!("✅ Workflow completed successfully\n");
println!("Key Features:");
println!(" • Multi-step approval workflow");
println!(" • Rejection and revision cycle");
println!(" • Version tracking across transitions");
println!(" • Terminal state with statistics\n");
}