1use super::{NotificationHook, StartParameter, Step, WorkflowDefinition, WorkflowInstance};
2use crate::common::Job;
3use mcai_graph::{Graph, GraphConfiguration, ToGraph};
4use semver::Version;
5use std::collections::HashMap;
6
7#[derive(PartialEq)]
8pub enum SchemaVersion {
9 _1_8,
10 _1_9,
11 _1_10,
12 _1_11,
13}
14
15#[derive(Clone, PartialEq)]
16pub enum Workflow {
17 Definition(WorkflowDefinition),
18 Instance(WorkflowInstance),
19}
20
21impl Workflow {
22 pub fn is_definition(&self) -> bool {
23 match self {
24 Workflow::Definition(_) => true,
25 Workflow::Instance(_) => false,
26 }
27 }
28
29 pub fn schema_version(&self) -> SchemaVersion {
30 match self {
31 Workflow::Definition(workflow) => workflow.schema_version(),
32 Workflow::Instance(workflow) => workflow.schema_version(),
33 }
34 }
35
36 pub fn identifier(&self) -> &str {
37 match self {
38 Workflow::Definition(workflow) => workflow.identifier(),
39 Workflow::Instance(workflow) => &workflow.identifier,
40 }
41 }
42
43 pub fn label(&self) -> &str {
44 match self {
45 Workflow::Definition(workflow) => workflow.label(),
46 Workflow::Instance(workflow) => &workflow.label,
47 }
48 }
49
50 pub fn version(&self) -> Version {
51 match self {
52 Workflow::Definition(workflow) => workflow.version(),
53 Workflow::Instance(workflow) => Version::new(
54 workflow.version_major as u64,
55 workflow.version_minor as u64,
56 workflow.version_micro as u64,
57 ),
58 }
59 }
60
61 pub fn is_live(&self) -> bool {
62 match self {
63 Workflow::Definition(workflow) => workflow.is_live(),
64 Workflow::Instance(workflow) => workflow.is_live,
65 }
66 }
67
68 pub fn tags(&self) -> &Vec<String> {
69 match self {
70 Workflow::Definition(workflow) => workflow.tags(),
71 Workflow::Instance(workflow) => &workflow.tags,
72 }
73 }
74
75 pub fn reference(&self) -> Option<String> {
76 match self {
77 Workflow::Definition(_) => None,
78 Workflow::Instance(workflow) => workflow.reference.clone(),
79 }
80 }
81
82 pub fn jobs(&self) -> Option<&Vec<Job>> {
83 match self {
84 Workflow::Definition(_) => None,
85 Workflow::Instance(workflow) => Some(&workflow.jobs),
86 }
87 }
88
89 pub fn steps(&self) -> &Vec<Step> {
90 match self {
91 Workflow::Definition(workflow) => workflow.steps(),
92 Workflow::Instance(workflow) => &workflow.steps,
93 }
94 }
95
96 pub fn get_start_parameters(&self) -> &Vec<StartParameter> {
97 match self {
98 Workflow::Definition(workflow) => workflow.get_start_parameters(),
99 Workflow::Instance(workflow) => &workflow.start_parameters,
100 }
101 }
102
103 pub fn get_notification_hooks(&self) -> Option<&Vec<NotificationHook>> {
104 match self {
105 Workflow::Definition(workflow) => workflow.get_notification_hooks(),
106 Workflow::Instance(_workflow) => None,
107 }
108 }
109
110 pub fn update_steps_coordinates_from_graph(&mut self, graph: &Graph) {
111 if let Workflow::Definition(workflow_definition) = self {
112 for (id, node) in graph.nodes() {
113 if let Some(step) = workflow_definition.get_mut_step(id) {
114 step.coordinates = Some(node.borrow().coordinates().clone());
115 }
116 continue;
117 }
118 }
119 }
120}
121
122impl ToGraph for Workflow {
123 fn to_graph(&self, configuration: GraphConfiguration) -> Graph {
124 let steps = self.steps().clone();
125
126 let mut graph = Graph::new(configuration.clone());
127
128 let mut nodes = HashMap::with_capacity(steps.len());
129 let mut node_level = HashMap::with_capacity(steps.len());
130
131 let mut level = 0;
132 let mut column = 0;
133
134 for step in self.steps().iter() {
135 if let Some(coordinates) = &step.coordinates {
137 let node = graph.add_node(step.id, coordinates.clone());
138 nodes.insert(step.id, node);
139 node_level.insert(step.id, level);
140 continue;
141 }
142
143 if step.parent_ids.is_empty() {
145 let node = graph.add_node(
146 step.id,
147 configuration
148 .node_configuration()
149 .get_coordinates(level, column),
150 );
151 nodes.insert(step.id, node);
152 node_level.insert(step.id, level);
153
154 column += 1;
155 }
156 }
157
158 loop {
159 column = 0;
160 if nodes.len() == steps.len() {
161 break;
162 }
163 level += 1;
164
165 for step in steps.iter() {
167 if nodes.contains_key(&step.id) {
168 continue;
169 }
170
171 if let Some(latest_level) = step
172 .parent_ids
173 .iter()
174 .filter_map(|parent_id| node_level.get(parent_id))
175 .max()
176 {
177 let step_level = latest_level + 1;
178 if step_level == level {
179 let node = graph.add_node(
180 step.id,
181 configuration
182 .node_configuration()
183 .get_coordinates(step_level, column),
184 );
185
186 nodes.insert(step.id, node);
187 node_level.insert(step.id, step_level);
188
189 column += 1;
190 }
191 }
192 }
193 }
194
195 for step in steps.iter() {
197 let parents = step
198 .parent_ids
199 .iter()
200 .filter_map(|parent_id| nodes.get(parent_id).cloned())
201 .collect();
202
203 let required_to_start = step
204 .required_to_start
205 .iter()
206 .filter_map(|required_step_id| nodes.get(required_step_id).cloned())
207 .collect();
208
209 if let Some(node) = nodes.get(&step.id) {
210 node.borrow_mut().set_parents(parents);
211 node.borrow_mut().set_required_nodes(required_to_start);
212 }
213 }
214
215 graph
216 }
217}