sz_rust_workflow/scheduling/
approval_strategy.rs1use crate::definition::ApprovalStrategyType;
5use crate::instance::{Task, TaskAction, TaskStatus};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum NodeCompletion {
10 Completed,
11 Rejected,
12 InProgress,
13}
14
15pub trait ApprovalStrategy: Send + Sync + 'static {
17 fn check_completion(&self, tasks: &[Task], action: TaskAction) -> NodeCompletion;
18}
19
20pub struct AndSignStrategy;
22
23impl ApprovalStrategy for AndSignStrategy {
24 fn check_completion(&self, tasks: &[Task], _action: TaskAction) -> NodeCompletion {
25 let relevant: Vec<&Task> = tasks
26 .iter()
27 .filter(|t| {
28 t.status == TaskStatus::Completed
29 || t.status == TaskStatus::Rejected
30 || t.status == TaskStatus::Pending
31 })
32 .collect();
33 if relevant.is_empty() {
34 return NodeCompletion::InProgress;
35 }
36 if relevant.iter().any(|t| t.status == TaskStatus::Rejected) {
37 return NodeCompletion::Rejected;
38 }
39 if relevant.iter().all(|t| t.status == TaskStatus::Completed) {
40 return NodeCompletion::Completed;
41 }
42 NodeCompletion::InProgress
43 }
44}
45
46pub struct OrSignStrategy;
48
49impl ApprovalStrategy for OrSignStrategy {
50 fn check_completion(&self, tasks: &[Task], _action: TaskAction) -> NodeCompletion {
51 if tasks.iter().any(|t| t.status == TaskStatus::Completed) {
52 return NodeCompletion::Completed;
53 }
54 if tasks.iter().all(|t| t.status == TaskStatus::Rejected) && !tasks.is_empty() {
55 return NodeCompletion::Rejected;
56 }
57 NodeCompletion::InProgress
58 }
59}
60
61pub fn select_strategy(strategy_type: ApprovalStrategyType) -> Box<dyn ApprovalStrategy> {
63 match strategy_type {
64 ApprovalStrategyType::AndSign => Box::new(AndSignStrategy),
65 ApprovalStrategyType::OrSign => Box::new(OrSignStrategy),
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use chrono::Utc;
73
74 fn make_task(id: &str, status: TaskStatus) -> Task {
75 Task {
76 task_id: id.into(),
77 instance_id: "i1".into(),
78 node_id: "n1".into(),
79 candidates: vec!["u1".into()],
80 status,
81 assignee: None,
82 action: None,
83 handled_at: None,
84 created_at: Utc::now(),
85 }
86 }
87
88 #[test]
89 fn and_sign_in_progress() {
90 let tasks = vec![
91 make_task("t1", TaskStatus::Completed),
92 make_task("t2", TaskStatus::Pending),
93 make_task("t3", TaskStatus::Pending),
94 ];
95 let s = AndSignStrategy;
96 assert_eq!(
97 s.check_completion(&tasks, TaskAction::Approve),
98 NodeCompletion::InProgress
99 );
100 }
101
102 #[test]
103 fn and_sign_completed() {
104 let tasks = vec![
105 make_task("t1", TaskStatus::Completed),
106 make_task("t2", TaskStatus::Completed),
107 make_task("t3", TaskStatus::Completed),
108 ];
109 let s = AndSignStrategy;
110 assert_eq!(
111 s.check_completion(&tasks, TaskAction::Approve),
112 NodeCompletion::Completed
113 );
114 }
115
116 #[test]
117 fn and_sign_rejected() {
118 let tasks = vec![
119 make_task("t1", TaskStatus::Completed),
120 make_task("t2", TaskStatus::Rejected),
121 ];
122 let s = AndSignStrategy;
123 assert_eq!(
124 s.check_completion(&tasks, TaskAction::Reject),
125 NodeCompletion::Rejected
126 );
127 }
128
129 #[test]
130 fn or_sign_completed() {
131 let tasks = vec![
132 make_task("t1", TaskStatus::Completed),
133 make_task("t2", TaskStatus::Pending),
134 ];
135 let s = OrSignStrategy;
136 assert_eq!(
137 s.check_completion(&tasks, TaskAction::Approve),
138 NodeCompletion::Completed
139 );
140 }
141
142 #[test]
143 fn or_sign_rejected() {
144 let tasks = vec![
145 make_task("t1", TaskStatus::Rejected),
146 make_task("t2", TaskStatus::Rejected),
147 ];
148 let s = OrSignStrategy;
149 assert_eq!(
150 s.check_completion(&tasks, TaskAction::Reject),
151 NodeCompletion::Rejected
152 );
153 }
154
155 #[test]
156 fn or_sign_in_progress() {
157 let tasks = vec![
158 make_task("t1", TaskStatus::Pending),
159 make_task("t2", TaskStatus::Pending),
160 ];
161 let s = OrSignStrategy;
162 assert_eq!(
163 s.check_completion(&tasks, TaskAction::Approve),
164 NodeCompletion::InProgress
165 );
166 }
167}