diff --git a/crates/icepick/src/cli/mod.rs b/crates/icepick/src/cli/mod.rs index 42894de..15b9731 100644 --- a/crates/icepick/src/cli/mod.rs +++ b/crates/icepick/src/cli/mod.rs @@ -107,8 +107,9 @@ pub fn do_cli_thing() { workflows.push((module.name.clone(), module.workflows.clone())); } let workflows = workflows.leak(); - let mut workflow_command = - clap::Command::new("workflow").about("Run a pre-defined Icepick workflow"); + let mut workflow_command = clap::Command::new("workflow") + .about("Run a pre-defined Icepick workflow") + .arg(clap::arg!(--"simulate-workflow").global(true)); for module in workflows.iter() { let mut module_subcommand = clap::Command::new(module.0.as_str()); for workflow in &module.1 { @@ -124,12 +125,16 @@ pub fn do_cli_thing() { for command in commands.iter() { let mut subcommand = clap::Command::new(command.0.as_str()); for op in &command.2 { - let mut op_command = clap::Command::new(op.name.replace('_', "-")).about(&op.description); + let mut op_command = + clap::Command::new(op.name.replace('_', "-")).about(&op.description); for arg in &op.arguments { - let mut op_arg = clap::Arg::new(arg.name.replace('_', "-")).help(arg.description.as_str()); + let mut op_arg = + clap::Arg::new(arg.name.replace('_', "-")).help(arg.description.as_str()); op_arg = match arg.r#type { ArgumentType::Required => op_arg.required(true), - ArgumentType::Optional => op_arg.required(false).long(arg.name.replace('_', "-")), + ArgumentType::Optional => { + op_arg.required(false).long(arg.name.replace('_', "-")) + } }; op_command = op_command.arg(op_arg); } diff --git a/crates/icepick/src/cli/workflow.rs b/crates/icepick/src/cli/workflow.rs index 97dd29e..9bb2f9c 100644 --- a/crates/icepick/src/cli/workflow.rs +++ b/crates/icepick/src/cli/workflow.rs @@ -82,6 +82,44 @@ impl Workflow { map } + pub fn simulate_workflow(&self, mut data: HashSet, operations: &[InvocableOperation]) { + // simulate the steps by using a HashSet to traverse the inputs and outputs and ensure + // there's no inconsistencies + for (i, step) in self.steps.iter().enumerate() { + // NOTE: overflow possible but unlikely + let step_index = i + 1; + + // Find the relevant Operation + let Some(invocable) = operations.iter().find(|op| op.name == step.r#type) else { + panic!("Could not find operation: {}", step.r#type); + }; + + // Check if we have the keys we want to pass into the module. + for in_memory_name in step.inputs.values() { + if !data.contains(in_memory_name) && !step.values.contains_key(in_memory_name) { + panic!("Failed simulation: step #{step_index}: missing value {in_memory_name}"); + } + } + + // Check that the module accepts those keys. + for module_input_name in step.inputs.keys() { + if !invocable + .operation + .arguments + .iter() + .any(|arg| *module_input_name == arg.name) + { + eprintln!("Simulation: step #{step_index}: input value {module_input_name} will be passed through as JSON input"); + } + } + + // Add the keys we get from the module. + for in_memory_name in step.outputs.values() { + data.insert(in_memory_name.clone()); + } + } + } + pub fn handle(&self, matches: &clap::ArgMatches, modules: Commands) { let inputs = self.load_inputs(matches); let data: HashMap = inputs @@ -103,43 +141,11 @@ impl Workflow { } } - // simulate the steps by using a HashSet to traverse the inputs and outputs and ensure - // there's no inconsistencies - let mut simulated_values = data.keys().collect::>(); - for (i, step) in self.steps.iter().enumerate() { - // NOTE: overflow possible but unlikely - let step_index = i + 1; - - // Find the relevant Operation - let Some(invocable) = operations.iter().find(|op| op.name == step.r#type) else { - panic!("Could not find operation: {}", step.r#type); - }; - - // Check if we have the keys we want to pass into the module. - for in_memory_name in step.inputs.values() { - if !simulated_values.contains(in_memory_name) - && !step.values.contains_key(in_memory_name) - { - panic!("Failed simulation: step #{step_index}: missing value {in_memory_name}"); - } - } - - // Check that the module accepts those keys. - for module_input_name in step.inputs.keys() { - if !invocable - .operation - .arguments - .iter() - .any(|arg| *module_input_name == arg.name) - { - eprintln!("Simulation: step #{step_index}: input value {module_input_name} will be passed through as JSON input"); - } - } - - // Add the keys we get from the module. - for in_memory_name in step.outputs.values() { - simulated_values.insert(in_memory_name); - } + if matches.get_flag("simulate-workflow") { + self.simulate_workflow(data.into_keys().collect(), &operations); + return; } + + todo!("Unsimulated transaction!"); } }