1use super::*;
2
3pub fn get_task_kind(kind: &str) -> Result<String, ErrorKind> {
15 match kind.to_lowercase().as_str() {
16 "openwhisk" => Ok("OpenWhisk".to_string()),
17 "polkadot" => Ok("Polkadot".to_string()),
18 "hello_world" => Ok("HelloWorldDerive".to_string()),
19 _ => Err(ErrorKind::NotFound),
20 }
21}
22
23fn get_main_method_code_template(tasks_length: usize) -> String {
24 format!(
25 "#[allow(dead_code, unused)]
26pub fn main(args: Value) -> Result<Value, String> {{
27 const LIMIT: usize = {tasks_length};
28 let mut workflow = WorkflowGraph::new(LIMIT);
29 let input: Input = serde_json::from_value(args).map_err(|e| e.to_string())?;
30"
31 )
32}
33
34pub fn get_attributes(attributes: &HashMap<String, String>) -> String {
47 let mut build_string = Vec::new();
48
49 for (key, value) in attributes {
50 build_string.push(format!("{}:\"{}\"", key.to_case(Case::Pascal), value));
51 }
52
53 format!("[{}]", build_string.join(","))
54}
55
56
57fn get_default_value_functions_code(workflow: &Workflow) -> String {
58 let mut default_value_functions = String::new();
59
60 for task in workflow.tasks.values() {
61 for input in task.input_arguments.iter() {
62 if !input.is_depend {
63 if let Some(val) = input.default_value.as_ref() {
64 let content = match input.input_type {
65 RustType::String => format!("{val:?}.to_string()"),
66 _ => format!(
67 "let val = serde_json::from_str::<{}>({:?}).unwrap();val",
68 input.input_type, val
69 ),
70 };
71
72 let make_fn = format!(
73 "pub fn {}_fn() -> {}{{{}}}\n",
74 input.name, input.input_type, content
75 );
76
77 default_value_functions.push_str(&make_fn);
78 }
79 };
80 }
81 }
82
83 default_value_functions
84}
85
86fn get_task_common_input_type_constructor(
99 composer_custom_types: &HashMap<String, String>,
100 workflow: &Workflow,
101) -> Result<String, Error> {
102 let mut common = Vec::<String>::new();
103 let mut workflow_custom_types = Vec::<String>::new();
104
105 for task in workflow.tasks.values() {
106 for input in task.input_arguments.iter() {
107 if let RustType::Struct(name) = &input.input_type {
108 workflow_custom_types.push(name.to_string());
109 }
110
111 if !input.is_depend {
112 if input.default_value.as_ref().is_some() {
113 common.push(format!(
114 "#[\"{}_fn\"] {}:{}",
115 input.name, input.name, input.input_type
116 ));
117 } else {
118 common.push(format!("{}:{}", input.name, input.input_type));
119 };
120 }
121 }
122 }
123
124 let workflow_custom_types = if !workflow_custom_types.is_empty() {
125 let mut build_string = String::new();
126
127 for custom_type in workflow_custom_types.iter() {
128 let typ = match composer_custom_types.get(custom_type) {
129 Some(t) => t,
130 None => return Err(Error::msg("Missing custom type in workflow")),
131 };
132 build_string = format!("{build_string}{typ}");
133 }
134
135 build_string
136 } else {
137 "".to_string()
138 };
139 Ok(format!(
140 "{workflow_custom_types}
141make_input_struct!(
142Input,
143[{}],
144[Debug, Clone, Default, Serialize, Deserialize]
145);",
146 common.join(",")
147 ))
148}
149
150fn get_task_type_constructors(workflow: &Workflow) -> String {
151 let mut constructors = String::new();
152
153 for task in workflow.tasks.values() {
154 let mut parameters = String::new();
155
156 for argument in task.input_arguments.iter() {
157 if !argument.is_depend {
158 parameters.push_str(&format!("input.{},", argument.name));
159 }
160 }
161
162 let constructor = format!(
163 "let {} = {}::new({}\"{}\".to_string());\n",
164 task.action_name.to_case(Case::Snake),
165 task.action_name.to_case(Case::Pascal),
166 parameters,
167 task.action_name.clone()
168 );
169
170 constructors.push_str(&constructor);
171 }
172
173 constructors
174}
175
176fn get_task_input_type_constructors(workflow: &Workflow) -> String {
177 let mut input_type_build_string = String::new();
178
179 for task in workflow.tasks.values() {
180 let mut arguments = Vec::new();
181
182 for field in task.input_arguments.iter() {
183 arguments.push(format!("{}:{}", field.name, field.input_type));
184 }
185
186 input_type_build_string.push_str(&format!(
187 "make_input_struct!(\n{}Input,\n[{}],\n[Debug, Clone, Default, Serialize, Deserialize]\n);",
188 task.action_name.to_case(Case::Pascal),
189 arguments.join(",")
190 ));
191 }
192
193 input_type_build_string
194}
195
196fn get_independent_fields(task: &Task) -> Vec<String> {
197 let mut independent_fields = Vec::<String>::new();
198
199 for field in task.input_arguments.iter() {
200 if !field.is_depend {
201 independent_fields.push(format!("{}:{}", field.name, field.input_type));
202 }
203 }
204
205 independent_fields
206}
207
208fn get_task_main_type_constructors(workflow: &Workflow) -> Result<String, Error> {
221 let mut input_structs = String::new();
222
223 for (task_name, task) in workflow.tasks.iter() {
224 let task_name = task_name.to_case(Case::Pascal);
225
226 let independent_fields = get_independent_fields(task);
227
228 let output_field = if task.operation.is_map() {
229 "mapout"
230 } else {
231 "output"
232 };
233
234 input_structs = format!(
235 "{input_structs}
236make_main_struct!(
237 {task_name},
238 {task_name}Input,
239 [Debug, Clone, Default, Serialize, Deserialize, {}],
240 {},
241 {}
242);
243impl_new!(
244 {task_name},
245 {task_name}Input,
246 [{}]
247);
248",
249 get_task_kind(&task.kind).unwrap(),
250 get_attributes(&task.attributes),
251 output_field,
252 independent_fields.join(",")
253 );
254 }
255
256 Ok(input_structs)
257}
258
259fn get_impl_setters_code(workflow: &Workflow) -> Result<String, Error> {
260 let mut impl_setters_code = String::new();
261
262 for (task_name, task) in workflow.tasks.iter() {
263 let task_name = task_name.to_case(Case::Pascal);
264
265 let mut setter_fields = Vec::<String>::new();
266
267 let mut set = HashMap::<String, i32>::new();
268 let mut index: i32 = 0;
269
270 for dependent in task.depend_on.iter() {
271 let current_index = if let Some(current_index) = set.get(&dependent.task_name) {
272 index -= 1;
273 current_index
274 } else {
275 set.insert(dependent.task_name.to_string(), index);
276 &index
277 };
278
279 if task.operation.is_combine() {
280 let dependent_task = match workflow.tasks.get(&dependent.task_name) {
281 Some(t) => t,
282 None => return Err(Error::msg("Missing custom type in workflow")),
283 };
284
285 if dependent_task.operation.is_map() {
286 setter_fields.push(format!(
287 "(value)[{}]{}:\"{}\"",
288 current_index, dependent.cur_field, dependent.prev_field
289 ));
290 } else {
291 setter_fields.push(format!(
292 "[{}]{}:\"{}\"",
293 current_index, dependent.cur_field, dependent.prev_field
294 ));
295 }
296 } else {
297 setter_fields.push(format!(
298 "{}:\"{}\"",
299 dependent.cur_field, dependent.prev_field
300 ));
301 }
302
303 index += 1;
304 }
305
306 let setter_build_string = match &task.operation {
307 Operation::Map(field) => format!(
308 "impl_map_setter!({}, {}, {}, \"{}\");\n",
309 task_name,
310 setter_fields.join(","),
311 task.input_arguments[0].input_type,
312 field
313 ),
314 Operation::Concat => format!(
315 "impl_concat_setter!({}, {});\n",
316 task_name, task.input_arguments[0].name
317 ),
318 Operation::Combine => format!(
319 "impl_combine_setter!({},[{}]);\n",
320 task_name,
321 setter_fields.join(","),
322 ),
323 _ => format!(
324 "impl_setter!({}, [{}]);\n",
325 task_name,
326 setter_fields.join(",")
327 ),
328 };
329
330 impl_setters_code.push_str(&setter_build_string);
331 }
332
333 Ok(impl_setters_code)
334}
335
336fn get_impl_execute_trait_code(workflow: &Workflow) -> String {
348 let mut task_names = Vec::new();
349
350 for task_name in workflow.tasks.keys() {
351 task_names.push(task_name.to_case(Case::Pascal));
352 }
353
354 format!("impl_execute_trait!({});", task_names.join(","))
355}
356
357fn get_add_nodes_code(flow: &Vec<String>) -> String {
358 let mut add_nodes_code = String::new();
359
360 for i in flow {
361 add_nodes_code.push_str(&format!(
362 "let {}_index = workflow.add_node(Box::new({}));\n",
363 i.to_case(Case::Snake),
364 i.to_case(Case::Snake)
365 ));
366 }
367
368 add_nodes_code
369}
370
371fn get_add_edges_code(workflow: &Workflow, flow: &Vec<String>) -> Result<String, Error> {
372 let mut add_edges_code = "workflow.add_edges(&[\n".to_string();
373
374 for index in 0..flow.len() - 1 {
375 if let Some(dependent_task) = workflow.tasks.get(&flow[index + 1]) {
376 let mut set = HashSet::<String>::new();
377
378 for dependent_task in dependent_task.depend_on.iter() {
379 if !set.contains(&dependent_task.task_name) {
380 add_edges_code = format!(
381 "{add_edges_code}({}_index, {}_index),\n",
382 dependent_task.task_name.to_case(Case::Snake),
383 flow[index + 1].to_case(Case::Snake)
384 );
385 set.insert(dependent_task.task_name.clone());
386 }
387 }
388 } else {
389 return Err(Error::msg(" Error adding the edges "));
390 }
391 }
392
393 add_edges_code += "]);";
394 Ok(add_edges_code)
395}
396
397fn get_add_execute_workflow_code(workflow: &Workflow, flow: &Vec<String>) -> Result<String, Error> {
398 let mut execute_code = "let result = workflow\n.init()?".to_string();
399
400 for task_index in 0..flow.len() - 1 {
401 execute_code = if task_index + 1 == flow.len() - 1 {
402 match workflow
403 .tasks
404 .get(&flow[task_index + 1])
405 .unwrap()
406 .depend_on
407 .len()
408 {
409 0 | 1 => {
410 format!(
411 "{execute_code}\n.term(Some({}_index))?;",
412 flow[task_index + 1].to_case(Case::Snake)
413 )
414 }
415
416 _ => {
417 format!(
418 "{execute_code}\n.pipe({}_index)?\n.term(None)?;",
419 flow[task_index + 1].to_case(Case::Snake)
420 )
421 }
422 }
423 } else {
424 format!(
425 "{execute_code}\n.pipe({}_index)?",
426 flow[task_index + 1].to_case(Case::Snake)
427 )
428 }
429 }
430
431 Ok(execute_code)
432}
433
434fn get_workflow_nodes_and_edges_code(workflow: &Workflow) -> Result<String, Error> {
445 let flow: Vec<String> = workflow.get_flow();
446
447 if flow.is_empty() {
448 return Ok("".to_string());
449 }
450
451 if flow.len() == 1 {
452 return Ok(format!(
453 "\
454let {}_index = workflow.add_node(Box::new({}));
455\tlet result = workflow\n\t\t.init()?
456\t\t.term(None)?;
457Ok(result)
458",
459 flow[0].to_case(Case::Snake),
460 flow[0].to_case(Case::Snake)
461 ));
462 }
463
464 Ok(format!(
465 "{}\n{}\n{}let result = serde_json::to_value(result).unwrap();\nOk(result)",
466 get_add_nodes_code(&flow),
467 get_add_edges_code(workflow, &flow)?,
468 get_add_execute_workflow_code(workflow, &flow)?,
469 ))
470}
471
472pub fn generate_types_rs_file_code(
483 workflow: &Workflow,
484 custom_types: &HashMap<String, String>,
485) -> Result<String, Error> {
486 let main_file = format!(
487 "use super::*;\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}}}",
488 add_polkadot_openwhisk(workflow),
489 get_task_input_type_constructors(workflow),
490 get_task_main_type_constructors(workflow)?,
491 get_impl_setters_code(workflow)?,
492 get_default_value_functions_code(workflow),
493 get_task_common_input_type_constructor(custom_types, workflow)?,
494 get_impl_execute_trait_code(workflow),
495 get_main_method_code_template(workflow.tasks.len()),
496 get_task_type_constructors(workflow),
497 get_workflow_nodes_and_edges_code(workflow)?
498 );
499 Ok(main_file)
500}
501
502fn get_openwhisk_kind_dependencies() -> String {
503 "
504openwhisk_macro = \"0.1.6\"
505
506"
507 .to_string()
508}
509
510fn get_polkadot_kind_dependencies() -> String {
511 "substrate_macro = \"0.1.3\"
513 pallet-staking = { git = \"https://github.com/paritytech/substrate.git\", package = \"pallet-staking\", rev = \"eb1a2a8\" }
514 substrate-api-client = { git = \"https://github.com/HugoByte/substrate-api-client.git\", default-features = false, features = [\"staking-xt\"], branch =\"wasm-support\"}
515sp-core = { version = \"6.0.0\", default-features = false, features = [\"full_crypto\"], git = \"https://github.com/paritytech/substrate.git\", rev = \"eb1a2a8\" }
516sp-runtime = { version = \"6.0.0\", default-features = false, git = \"https://github.com/paritytech/substrate.git\", rev = \"eb1a2a8\" }
517 "
518 .to_string()
519}
520
521pub fn generate_cargo_toml_dependencies(workflow: &Workflow) -> String {
522 let mut dependency_map = HashMap::new();
523
524 let hello_world_dependency = "hello_world_macro = {git= \"https://github.com/HugoByte/aurras.git\", branch = \"next\", package = \"hello_world_macro\"}"
525 .to_string();
526
527 dependency_map.insert("hello_world", hello_world_dependency);
528 dependency_map.insert("openwhisk", get_openwhisk_kind_dependencies());
529 dependency_map.insert("polkadot", get_polkadot_kind_dependencies());
530
531 let kinds = get_common_kind(workflow);
532 if kinds.is_empty() {
533 return String::new();
534 }
535
536 let mut toml_dependencies = String::new();
537 for (kind, dependency_string) in dependency_map.iter() {
538 if kinds.contains(&kind.to_string()) {
539 toml_dependencies.push_str(dependency_string);
540 }
541 }
542
543 toml_dependencies
544}
545
546pub fn handle_multiple_dependency() -> String {
547 let openwhisk_dependency = get_openwhisk_kind_dependencies();
548 let polkadot_dependency = get_polkadot_kind_dependencies();
549
550 let combined_dependencies = format!("{}{}", openwhisk_dependency, polkadot_dependency);
551 combined_dependencies
552}
553
554pub fn get_polkadot() -> String {
555 "\
556 use substrate_macro::Polkadot;
557 use sp_core::H256;
558
559 "
560 .to_string()
561}
562
563pub fn get_openwhisk() -> String {
564 "\
565 use openwhisk_macro::*;
566 use openwhisk_rust::*;
567
568 "
569 .to_string()
570}
571
572pub fn add_polkadot_openwhisk(workflow: &Workflow) -> String {
573 let kinds = get_common_kind(workflow);
574
575 let mut toml_dependencies = String::new();
576
577 if kinds.contains("openwhisk") {
578 toml_dependencies = get_openwhisk();
579 }
580
581 if kinds.contains("polkadot") {
582 toml_dependencies = get_polkadot();
583 }
584
585 if kinds.contains("openwhisk") && kinds.contains("polkadot") {
586 toml_dependencies = handle_multiple_kinds();
587 }
588
589 if kinds.contains("hello_world") {
590 toml_dependencies += "\nuse hello_world_macro::HelloWorldDerive;";
591 }
592
593 toml_dependencies
594}
595
596pub fn staking_ledger() -> String {
597 "\
598use sp_runtime::AccountId32;
599
600#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)]
601 pub struct StakingLedger {
602 pub stash: AccountId32,
603 #[codec(compact)]
604 pub total: u128,
605 #[codec(compact)]
606 pub active: u128,
607 pub unlocking: Vec<u32>,
608 pub claimed_rewards: Vec<u32>,
609}
610 "
611 .to_string()
612}
613
614pub fn get_struct_stake_ledger(workflow: &Workflow) -> String {
615 let kinds = get_common_kind(workflow);
616
617 let mut toml_dependencies = String::new();
618
619 if kinds.contains("polkadot") {
620 toml_dependencies = staking_ledger();
621 }
622
623 toml_dependencies
624}
625
626pub fn get_common_kind(workflow: &Workflow) -> HashSet<String> {
627 let mut kinds = HashSet::new();
628 for task in workflow.tasks.values() {
629 kinds.insert(task.kind.to_lowercase());
630 }
631 kinds
632}
633
634pub fn handle_multiple_kinds() -> String {
635 let openwhisk = get_openwhisk();
636 let polkadot = get_polkadot();
637
638 let combined_dependencies = format!("{}{}", openwhisk, polkadot);
639 combined_dependencies
640}
641
642#[cfg(test)]
643mod tests {
644 use super::*;
645
646 #[test]
647 fn test_get_main_method_code_template() {
648 let output = get_main_method_code_template(4);
649
650 assert_eq!(
651 &output,
652 "#[allow(dead_code, unused)]
653pub fn main(args: Value) -> Result<Value, String> {
654 const LIMIT: usize = 4;
655 let mut workflow = WorkflowGraph::new(LIMIT);
656 let input: Input = serde_json::from_value(args).map_err(|e| e.to_string())?;
657"
658 );
659 }
660
661 #[test]
662 fn test_get_attributes() {
663 let mut attributes = HashMap::new();
664 attributes.insert("key".to_string(), "value".to_string());
665
666 let output = get_attributes(&attributes);
667 assert_eq!(output, "[Key:\"value\"]");
668
669 }
670
671 #[test]
672 fn test_get_default_value_functions_code() {
673 let task1 = Task {
674 action_name: "task0".to_string(),
675 input_arguments: vec![
676 Input {
677 name: "argument_1".to_string(),
678 input_type: RustType::String,
679 default_value: Some("value_x".to_string()),
680 ..Default::default()
681 },
682 Input {
683 name: "argument_2".to_string(),
684 input_type: RustType::List(Box::new(RustType::String)),
685 default_value: Some("[\"val1,\"val2\"]".to_string()),
686 ..Default::default()
687 },
688 ],
689 ..Default::default()
690 };
691
692 let mut tasks = HashMap::new();
693 tasks.insert("task1".to_string(), task1);
694
695 let workflow = Workflow {
696 name: "test-workflow".to_string(),
697 version: "0.0.1".to_string(),
698 tasks,
699 };
700
701 let output = get_default_value_functions_code(&workflow);
702
703 assert_eq!(
704 output,
705 "\
706pub fn argument_1_fn() -> String{\"value_x\".to_string()}
707pub fn argument_2_fn() -> Vec<String>{let val = serde_json::from_str::<Vec<String>>(\"[\\\"val1,\\\"val2\\\"]\").unwrap();val}
708"
709 )
710 }
711
712 #[test]
713 fn test_get_task_common_input_type_constructor() {
714 let task0 = Task {
715 action_name: "task0".to_string(),
716 input_arguments: vec![
717 Input {
718 name: "argument_1".to_string(),
719 input_type: RustType::Boolean,
720 is_depend: true,
721 ..Default::default()
722 },
723 Input {
724 name: "argument_2".to_string(),
725 input_type: RustType::Int,
726 ..Default::default()
727 },
728 Input {
729 name: "argument_3".to_string(),
730 input_type: RustType::List(Box::new(RustType::Uint)),
731 ..Default::default()
732 },
733 Input {
734 name: "argument_4".to_string(),
735 input_type: RustType::Float,
736 is_depend: true,
737 ..Default::default()
738 },
739 Input {
740 name: "argument_5".to_string(),
741 input_type: RustType::String,
742 ..Default::default()
743 },
744 Input {
745 name: "argument_6".to_string(),
746 input_type: RustType::HashMap(
747 Box::new(RustType::Int),
748 Box::new(RustType::Float),
749 ),
750 ..Default::default()
751 },
752 Input {
753 name: "argument_7".to_string(),
754 input_type: RustType::Tuple(Box::new(RustType::Int), Box::new(RustType::Float)),
755 ..Default::default()
756 },
757 Input {
758 name: "argument_8".to_string(),
759 input_type: RustType::Struct("Struct1".to_string()),
760 ..Default::default()
761 },
762 ],
763 ..Default::default()
764 };
765
766 let mut tasks = HashMap::new();
767 tasks.insert("task0".to_string(), task0);
768
769 let workflow = Workflow {
770 name: "test-workflow".to_string(),
771 version: "0.0.1".to_string(),
772 tasks,
773 };
774
775 let mut custom_types = HashMap::new();
776
777 custom_types.insert(
778 "Struct1".to_string(),
779 "make_input_struct!(\nStruct1,\n{field1:i32},\n[Default, Clone, Debug, Deserialize, Serialize]\n);".to_string());
780
781 let output = get_task_common_input_type_constructor(&custom_types, &workflow);
782 assert_eq!(
783 &output.unwrap(),
784 "\
785make_input_struct!(
786Struct1,
787{field1:i32},
788[Default, Clone, Debug, Deserialize, Serialize]
789);
790make_input_struct!(
791Input,
792[argument_2:i32,argument_3:Vec<u32>,argument_5:String,argument_6:HashMap<i32,f32>,argument_7:(i32,f32),argument_8:Struct1],
793[Debug, Clone, Default, Serialize, Deserialize]
794);")
795 }
796
797 #[test]
798 fn test_get_task_type_constructors() {
799 let task0 = Task {
800 action_name: "task0".to_string(),
801 input_arguments: vec![
802 Input {
803 name: "argument_1".to_string(),
804 input_type: RustType::Boolean,
805 ..Default::default()
806 },
807 Input {
808 name: "argument_2".to_string(),
809 input_type: RustType::Int,
810 ..Default::default()
811 },
812 ],
813 ..Default::default()
814 };
815
816 let mut tasks = HashMap::new();
817 tasks.insert("task0".to_string(), task0);
818
819 let workflow = Workflow {
820 name: "test-workflow".to_string(),
821 version: "0.0.1".to_string(),
822 tasks,
823 };
824
825 let output = get_task_type_constructors(&workflow);
826
827 assert_eq!(
828 output,
829 "let task_0 = Task0::new(input.argument_1,input.argument_2,\"task0\".to_string());\n"
830 );
831 }
832
833 #[test]
834 fn test_get_task_input_type_constructors() {
835 let task0 = Task {
836 action_name: "task0".to_string(),
837 input_arguments: vec![
838 Input {
839 name: "argument_1".to_string(),
840 input_type: RustType::Boolean,
841 ..Default::default()
842 },
843 Input {
844 name: "argument_2".to_string(),
845 input_type: RustType::Int,
846 ..Default::default()
847 },
848 ],
849 ..Default::default()
850 };
851
852 let mut tasks = HashMap::new();
853 tasks.insert("task0".to_string(), task0);
854
855 let workflow = Workflow {
856 name: "test-workflow".to_string(),
857 version: "0.0.1".to_string(),
858 tasks,
859 };
860
861 let output = get_task_input_type_constructors(&workflow);
862
863 println!("{:?}", output);
864
865 assert_eq!(
866 output,
867 "make_input_struct!(
868Task0Input,
869[argument_1:bool,argument_2:i32],
870[Debug, Clone, Default, Serialize, Deserialize]
871);"
872 );
873 }
874
875 #[test]
876 fn test_get_independent_fields() {
877 let task0 = Task {
878 action_name: "task0".to_string(),
879 input_arguments: vec![
880 Input {
881 name: "argument_1".to_string(),
882 input_type: RustType::Boolean,
883 is_depend: true,
884 ..Default::default()
885 },
886 Input {
887 name: "argument_2".to_string(),
888 input_type: RustType::Int,
889 ..Default::default()
890 },
891 ],
892 ..Default::default()
893 };
894
895 let output = get_independent_fields(&task0);
896
897 assert_eq!(output, vec!["argument_2:i32"]);
898 }
899
900 #[test]
901 fn test_get_task_main_type_constructors() {
902 let task0 = Task {
903 action_name: "task0".to_string(),
904 kind: "Openwhisk".to_string(),
905 input_arguments: vec![
906 Input {
907 name: "argument_1".to_string(),
908 input_type: RustType::Boolean,
909 ..Default::default()
910 },
911 Input {
912 name: "argument_2".to_string(),
913 input_type: RustType::Int,
914 ..Default::default()
915 },
916 ],
917 ..Default::default()
918 };
919
920 let mut tasks = HashMap::new();
921 tasks.insert("task0".to_string(), task0);
922
923 let workflow = Workflow {
924 name: "test-workflow".to_string(),
925 version: "0.0.1".to_string(),
926 tasks,
927 };
928
929 let output = get_task_main_type_constructors(&workflow);
930
931 assert_eq!(
932 output.unwrap(),
933 "
934make_main_struct!(
935 Task0,
936 Task0Input,
937 [Debug, Clone, Default, Serialize, Deserialize, OpenWhisk],
938 [],
939 output
940);
941impl_new!(
942 Task0,
943 Task0Input,
944 [argument_1:bool,argument_2:i32]
945);
946"
947 );
948 }
949
950 #[test]
951 fn test_get_impl_setters_code() {
952 let task0 = Task {
953 action_name: "task0".to_string(),
954 kind: "Openwhisk".to_string(),
955 input_arguments: vec![
956 Input {
957 name: "argument_1".to_string(),
958 input_type: RustType::Boolean,
959 is_depend: true,
960 ..Default::default()
961 },
962 Input {
963 name: "argument_2".to_string(),
964 input_type: RustType::Int,
965 ..Default::default()
966 },
967 ],
968 depend_on: vec![Depend {
969 task_name: "task1".to_string(),
970 cur_field: "argument_1".to_string(),
971 prev_field: "data_field".to_string(),
972 }],
973 ..Default::default()
974 };
975
976 let mut tasks = HashMap::new();
977 tasks.insert("task0".to_string(), task0);
978
979 let workflow = Workflow {
980 name: "test-workflow".to_string(),
981 version: "0.0.1".to_string(),
982 tasks,
983 };
984
985 let output = get_impl_setters_code(&workflow);
986
987 assert_eq!(
988 output.unwrap(),
989 "impl_setter!(Task0, [argument_1:\"data_field\"]);\n"
990 );
991 }
992
993 #[test]
994 fn test_get_impl_execute_trait_code() {
995 let task0 = Task {
996 action_name: "task0".to_string(),
997 kind: "Openwhisk".to_string(),
998 ..Default::default()
999 };
1000
1001 let task1 = Task {
1002 action_name: "task1".to_string(),
1003 kind: "Openwhisk".to_string(),
1004 ..Default::default()
1005 };
1006
1007 let mut tasks = HashMap::new();
1008 tasks.insert("task0".to_string(), task0);
1009 tasks.insert("task1".to_string(), task1);
1010
1011 let workflow = Workflow {
1012 name: "test-workflow".to_string(),
1013 version: "0.0.1".to_string(),
1014 tasks,
1015 };
1016
1017 let output = get_impl_execute_trait_code(&workflow);
1018 assert!(
1019 output == "impl_execute_trait!(Task0,Task1);"
1020 || output == "impl_execute_trait!(Task1,Task0);"
1021 );
1022 }
1023
1024 #[test]
1025 fn test_get_add_nodes_code() {
1026 let flow = vec![
1027 "task0".to_string(),
1028 "task2".to_string(),
1029 "task1".to_string(),
1030 "task4".to_string(),
1031 "task3".to_string(),
1032 ];
1033
1034 let output = get_add_nodes_code(&flow);
1035
1036 assert_eq!(
1037 output,
1038 "\
1039let task_0_index = workflow.add_node(Box::new(task_0));
1040let task_2_index = workflow.add_node(Box::new(task_2));
1041let task_1_index = workflow.add_node(Box::new(task_1));
1042let task_4_index = workflow.add_node(Box::new(task_4));
1043let task_3_index = workflow.add_node(Box::new(task_3));
1044"
1045 )
1046 }
1047
1048 #[test]
1049 fn test_get_add_edges_code() {
1050 let task0 = Task {
1051 action_name: "task0".to_string(),
1052 ..Default::default()
1053 };
1054 let task1 = Task {
1055 action_name: "task1".to_string(),
1056 depend_on: vec![Depend {
1057 task_name: "task0".to_string(),
1058 ..Default::default()
1059 }],
1060 ..Default::default()
1061 };
1062
1063 let task2 = Task {
1064 action_name: "task2".to_string(),
1065 depend_on: vec![
1066 Depend {
1067 task_name: "task1".to_string(),
1068 ..Default::default()
1069 },
1070 Depend {
1071 task_name: "task0".to_string(),
1072 ..Default::default()
1073 },
1074 ],
1075 ..Default::default()
1076 };
1077
1078 let task3 = Task {
1079 action_name: "task3".to_string(),
1080 depend_on: vec![Depend {
1081 task_name: "task2".to_string(),
1082 ..Default::default()
1083 }],
1084 ..Default::default()
1085 };
1086
1087 let task4 = Task {
1088 action_name: "task4".to_string(),
1089 depend_on: vec![
1090 Depend {
1091 task_name: "task3".to_string(),
1092 ..Default::default()
1093 },
1094 Depend {
1095 task_name: "task2".to_string(),
1096 ..Default::default()
1097 },
1098 ],
1099 ..Default::default()
1100 };
1101
1102 let mut tasks = HashMap::new();
1103 tasks.insert("task0".to_string(), task0);
1104 tasks.insert("task1".to_string(), task1);
1105 tasks.insert("task2".to_string(), task2);
1106 tasks.insert("task3".to_string(), task3);
1107 tasks.insert("task4".to_string(), task4);
1108
1109 let workflow = Workflow {
1110 name: "test-workflow".to_string(),
1111 version: "0.0.1".to_string(),
1112 tasks,
1113 };
1114
1115 let flow = workflow.get_flow();
1116
1117 let output = get_add_edges_code(&workflow, &flow);
1118
1119 assert_eq!(
1120 output.unwrap(),
1121 "\
1122workflow.add_edges(&[
1123(task_0_index, task_1_index),
1124(task_1_index, task_2_index),
1125(task_0_index, task_2_index),
1126(task_2_index, task_3_index),
1127(task_3_index, task_4_index),
1128(task_2_index, task_4_index),
1129]);"
1130 );
1131 }
1132
1133 #[test]
1134 fn test_get_add_execute_workflow_code() {
1135 let task0 = Task {
1136 action_name: "task0".to_string(),
1137 ..Default::default()
1138 };
1139 let task1 = Task {
1140 action_name: "task1".to_string(),
1141 depend_on: vec![Depend {
1142 task_name: "task0".to_string(),
1143 ..Default::default()
1144 }],
1145 ..Default::default()
1146 };
1147
1148 let task2 = Task {
1149 action_name: "task2".to_string(),
1150 depend_on: vec![
1151 Depend {
1152 task_name: "task1".to_string(),
1153 ..Default::default()
1154 },
1155 Depend {
1156 task_name: "task0".to_string(),
1157 ..Default::default()
1158 },
1159 ],
1160 ..Default::default()
1161 };
1162
1163 let task3 = Task {
1164 action_name: "task3".to_string(),
1165 depend_on: vec![Depend {
1166 task_name: "task2".to_string(),
1167 ..Default::default()
1168 }],
1169 ..Default::default()
1170 };
1171
1172 let task4 = Task {
1173 action_name: "task4".to_string(),
1174 depend_on: vec![
1175 Depend {
1176 task_name: "task3".to_string(),
1177 ..Default::default()
1178 },
1179 Depend {
1180 task_name: "task2".to_string(),
1181 ..Default::default()
1182 },
1183 ],
1184 ..Default::default()
1185 };
1186
1187 let mut tasks = HashMap::new();
1188 tasks.insert("task0".to_string(), task0);
1189 tasks.insert("task1".to_string(), task1);
1190 tasks.insert("task2".to_string(), task2);
1191 tasks.insert("task3".to_string(), task3);
1192 tasks.insert("task4".to_string(), task4);
1193
1194 let workflow = Workflow {
1195 name: "test-workflow".to_string(),
1196 version: "0.0.1".to_string(),
1197 tasks,
1198 };
1199
1200 let flow = workflow.get_flow();
1201
1202 let output = get_add_execute_workflow_code(&workflow, &flow);
1203
1204 assert_eq!(
1205 output.unwrap(),
1206 "\
1207let result = workflow
1208.init()?
1209.pipe(task_1_index)?
1210.pipe(task_2_index)?
1211.pipe(task_3_index)?
1212.pipe(task_4_index)?
1213.term(None)?;"
1214 );
1215 }
1216}