phlow_engine/
transform.rs

1use crate::{
2    phlow::PipelineMap,
3    pipeline::Pipeline,
4    step_worker::{StepReference, StepWorker, StepWorkerError},
5};
6use phlow_sdk::{prelude::*, valu3};
7use rhai::Engine;
8use std::sync::Arc;
9use std::{collections::HashMap, fmt::Display};
10use valu3::{traits::ToValueBehavior, value::Value};
11
12#[derive(Debug)]
13pub enum TransformError {
14    InnerStepError(StepWorkerError),
15    Parser(valu3::Error),
16}
17
18impl Display for TransformError {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match self {
21            TransformError::InnerStepError(err) => write!(f, "Inner step error: {}", err),
22            TransformError::Parser(_) => write!(f, "Parser error: Non parseable"),
23        }
24    }
25}
26
27pub(crate) fn value_to_pipelines(
28    engine: Arc<Engine>,
29    modules: Arc<Modules>,
30    input: &Value,
31) -> Result<PipelineMap, TransformError> {
32    let mut map = Vec::new();
33
34    process_raw_steps(input, &mut map);
35    value_to_structs(engine, modules, &map)
36}
37
38pub(crate) fn process_raw_steps(input: &Value, map: &mut Vec<Value>) -> Value {
39    if let Value::Object(pipeline) = input {
40        let mut new_pipeline = pipeline.clone();
41
42        new_pipeline.remove(&"steps");
43
44        // Tratamento para THEN
45        if let Some(then) = pipeline.get("then") {
46            let then_value = process_raw_steps(then, map);
47            new_pipeline.insert("then".to_string(), then_value);
48        }
49
50        // Tratamento para ELSE
51        if let Some(els) = pipeline.get("else") {
52            let else_value = process_raw_steps(els, map);
53            new_pipeline.insert("else".to_string(), else_value);
54        }
55
56        let mut new_steps = if new_pipeline.is_empty() {
57            vec![]
58        } else {
59            vec![new_pipeline.to_value()]
60        };
61
62        if let Some(steps) = pipeline.get("steps") {
63            if let Value::Array(steps) = steps {
64                for step in steps {
65                    let mut new_step = step.clone();
66
67                    if let Some(then) = step.get("then") {
68                        new_step.insert("then".to_string(), process_raw_steps(then, map));
69                    }
70
71                    if let Some(els) = step.get("else") {
72                        new_step.insert("else".to_string(), process_raw_steps(els, map));
73                    }
74
75                    new_steps.push(new_step);
76                }
77            }
78        }
79
80        map.push(new_steps.to_value());
81    } else if let Value::Array(pipeline) = input {
82        let mut new_steps = Vec::new();
83
84        for step in pipeline {
85            if let Value::Object(step) = step {
86                let mut new_step = step.clone();
87
88                if let Some(then) = step.get("then") {
89                    new_step.insert("then".to_string(), process_raw_steps(then, map));
90                }
91
92                if let Some(els) = step.get("else") {
93                    new_step.insert("else".to_string(), process_raw_steps(els, map));
94                }
95
96                new_steps.push(new_step);
97            }
98        }
99
100        map.push(new_steps.to_value());
101    }
102
103    (map.len() - 1).to_value()
104}
105
106/// Function to transform a value into a pipeline map
107/// This function takes a value and transforms it into a pipeline map.
108/// It uses the `value_to_structs` function to convert the value into a pipeline map.
109/// It also uses the `resolve_go_to_step` function to resolve the "go to" step.
110/// The function returns a `Result` with the pipeline map or an error.
111fn value_to_structs(
112    engine: Arc<Engine>,
113    modules: Arc<Modules>,
114    pipelines_raw: &Vec<Value>,
115) -> Result<PipelineMap, TransformError> {
116    let (parents, go_to_step_id) = map_parents(pipelines_raw);
117    let mut pipelines = HashMap::new();
118
119    for (pipeline_index, pipeline_value) in pipelines_raw.iter().enumerate() {
120        if let Value::Array(arr) = pipeline_value {
121            let mut steps = Vec::new();
122
123            for (step_index, step_value) in arr.into_iter().enumerate() {
124                if let Value::Object(step) = step_value {
125                    let mut new_step = step.clone();
126
127                    if let Some(to) = step.get("to") {
128                        if let Some(go_to_step) = go_to_step_id.get(to.to_string().as_str()) {
129                            new_step.insert("to".to_string(), go_to_step.to_value());
130                        }
131                    } else {
132                        if step.get("then").is_none()
133                            && step.get("else").is_none()
134                            && step.get("return").is_none()
135                        {
136                            if let Some(target) = parents.get(&StepReference {
137                                pipeline: pipeline_index,
138                                step: step_index,
139                            }) {
140                                let next_step = get_next_step(&pipelines_raw, target);
141                                new_step.insert("to".to_string(), next_step.to_value());
142                            }
143                        }
144                    }
145
146                    let step_worker = StepWorker::try_from_value(
147                        engine.clone(),
148                        modules.clone(),
149                        &new_step.to_value(),
150                    )
151                    .map_err(TransformError::InnerStepError)?;
152                    steps.push(step_worker);
153                }
154            }
155
156            pipelines.insert(pipeline_index, Pipeline { steps });
157        }
158    }
159
160    Ok(pipelines)
161}
162
163/// Function to get the next step
164/// This function takes a vector of pipelines and a target step reference.
165fn get_next_step(pipelines: &Vec<Value>, target: &StepReference) -> StepReference {
166    if let Value::Array(arr) = &pipelines[target.pipeline] {
167        let next_step_index = target.step + 1;
168        if arr.get(next_step_index).is_some() {
169            return StepReference {
170                pipeline: target.pipeline,
171                step: next_step_index,
172            };
173        }
174    }
175
176    return StepReference {
177        pipeline: target.pipeline,
178        step: target.step,
179    };
180}
181
182/// Function to map parents
183/// This function takes a vector of pipelines and builds a parent map.
184fn map_parents(
185    pipelines: &Vec<Value>,
186) -> (
187    HashMap<StepReference, StepReference>,
188    HashMap<String, StepReference>,
189) {
190    let (parents, go_to_step_references) = build_parent_map(pipelines);
191    (resolve_final_parents(parents), go_to_step_references)
192}
193
194/// Function to build the parent map
195/// This function takes a vector of pipelines and builds a parent map.
196/// It uses a hashmap to store the step references.
197fn build_parent_map(
198    pipelines: &Vec<Value>,
199) -> (
200    HashMap<StepReference, StepReference>,
201    HashMap<String, StepReference>,
202) {
203    let mut parents = HashMap::new();
204    let mut go_to_step_id = HashMap::new();
205
206    for (pipeline_index, steps) in pipelines.iter().enumerate() {
207        if let Value::Array(arr) = steps {
208            for (step_index, step) in arr.into_iter().enumerate() {
209                if let Value::Object(step) = step {
210                    if let Some(id) = step.get("id") {
211                        go_to_step_id.insert(
212                            id.to_string(),
213                            StepReference {
214                                pipeline: pipeline_index,
215                                step: step_index,
216                            },
217                        );
218                    }
219
220                    // Adiciona relações de "then" e "else" ao mapa de pais
221                    if let Some(then_value) = step.get("then").and_then(|v| v.to_u64()) {
222                        parents.insert(
223                            StepReference {
224                                pipeline: then_value as usize,
225                                step: 0,
226                            },
227                            StepReference {
228                                pipeline: pipeline_index,
229                                step: step_index,
230                            },
231                        );
232                    }
233
234                    if let Some(else_value) = step.get("else").and_then(|v| v.to_u64()) {
235                        parents.insert(
236                            StepReference {
237                                pipeline: else_value as usize,
238                                step: 0,
239                            },
240                            StepReference {
241                                pipeline: pipeline_index,
242                                step: step_index,
243                            },
244                        );
245                    }
246                }
247            }
248        }
249    }
250
251    (parents, go_to_step_id)
252}
253
254/// Function to resolve final parents
255/// This function takes a parent map and resolves the final parents.
256fn resolve_final_parents(
257    parents: HashMap<StepReference, StepReference>,
258) -> HashMap<StepReference, StepReference> {
259    let mut final_parents = HashMap::new();
260
261    for (child, mut parent) in parents.iter() {
262        // Resolve o pai final seguindo a cadeia de ancestrais
263        while let Some(grandparent) = parents.get(parent) {
264            parent = grandparent;
265        }
266        final_parents.insert(child.clone(), parent.clone());
267    }
268
269    final_parents
270}