diff --git a/src/extension/clipboard.rs b/src/extension/clipboard.rs index 9b5477b..d82112d 100644 --- a/src/extension/clipboard.rs +++ b/src/extension/clipboard.rs @@ -22,6 +22,8 @@ use crate::extension::ExtensionResult; use serde_yaml::Mapping; use std::collections::HashMap; +use super::ExtensionOut; + pub struct ClipboardExtension { clipboard_manager: Box, } @@ -42,11 +44,11 @@ impl super::Extension for ClipboardExtension { _: &Mapping, _: &Vec, _: &HashMap, - ) -> Option { + ) -> ExtensionOut { if let Some(clipboard) = self.clipboard_manager.get_clipboard() { - Some(ExtensionResult::Single(clipboard)) + Ok(Some(ExtensionResult::Single(clipboard))) } else { - None + Ok(None) } } } diff --git a/src/extension/date.rs b/src/extension/date.rs index 2e4324b..90e1ecc 100644 --- a/src/extension/date.rs +++ b/src/extension/date.rs @@ -22,6 +22,8 @@ use chrono::{DateTime, Duration, Local}; use serde_yaml::{Mapping, Value}; use std::collections::HashMap; +use super::ExtensionOut; + pub struct DateExtension {} impl DateExtension { @@ -40,7 +42,7 @@ impl super::Extension for DateExtension { params: &Mapping, _: &Vec, _: &HashMap, - ) -> Option { + ) -> ExtensionOut { let mut now: DateTime = Local::now(); // Compute the given offset @@ -59,6 +61,6 @@ impl super::Extension for DateExtension { now.to_rfc2822() }; - Some(ExtensionResult::Single(date)) + Ok(Some(ExtensionResult::Single(date))) } } diff --git a/src/extension/dummy.rs b/src/extension/dummy.rs index e810823..030b71d 100644 --- a/src/extension/dummy.rs +++ b/src/extension/dummy.rs @@ -43,15 +43,15 @@ impl super::Extension for DummyExtension { params: &Mapping, _: &Vec, _: &HashMap, - ) -> Option { + ) -> super::ExtensionOut { let echo = params.get(&Value::from("echo")); if let Some(echo) = echo { - Some(ExtensionResult::Single( + Ok(Some(ExtensionResult::Single( echo.as_str().unwrap_or_default().to_owned(), - )) + ))) } else { - None + Ok(None) } } } diff --git a/src/extension/form.rs b/src/extension/form.rs index 00ac091..888130f 100644 --- a/src/extension/form.rs +++ b/src/extension/form.rs @@ -43,13 +43,13 @@ impl super::Extension for FormExtension { params: &Mapping, _: &Vec, _: &HashMap, - ) -> Option { + ) -> super::ExtensionOut { let layout = params.get(&Value::from("layout")); let layout = if let Some(value) = layout { value.as_str().unwrap_or_default().to_string() } else { error!("invoking form extension without specifying a layout"); - return None; + return Err(super::ExtensionError::Internal); }; let mut form_config = Mapping::new(); @@ -81,16 +81,22 @@ impl super::Extension for FormExtension { let json: Result, _> = serde_json::from_str(&output); match json { Ok(json) => { - return Some(ExtensionResult::Multiple(json)); + // Check if the JSON is empty. In those cases, it means the user exited + // the form before submitting it, therefore the expansion should stop + if json.is_empty() { + return Err(super::ExtensionError::Aborted); + } + + return Ok(Some(ExtensionResult::Multiple(json))); } Err(error) => { error!("modulo json parsing error: {}", error); - return None; + return Err(super::ExtensionError::Internal); } } } else { error!("modulo form didn't return any output"); - return None; + return Err(super::ExtensionError::Internal); } } } diff --git a/src/extension/mod.rs b/src/extension/mod.rs index 7ea9546..532c902 100644 --- a/src/extension/mod.rs +++ b/src/extension/mod.rs @@ -38,6 +38,17 @@ pub enum ExtensionResult { Multiple(HashMap), } +#[derive(Clone, Debug, PartialEq)] +pub enum ExtensionError { + // Returned by an extension if an internal process occurred + Internal, + // Returned by an extension if the user aborted the expansion + // for example when pressing ESC inside a FormExtension. + Aborted, +} + +pub type ExtensionOut = Result, ExtensionError>; + pub trait Extension { fn name(&self) -> String; fn calculate( @@ -45,7 +56,7 @@ pub trait Extension { params: &Mapping, args: &Vec, current_vars: &HashMap, - ) -> Option; + ) -> ExtensionOut; } pub fn get_extensions( diff --git a/src/extension/multiecho.rs b/src/extension/multiecho.rs index e48eaf8..99ec02f 100644 --- a/src/extension/multiecho.rs +++ b/src/extension/multiecho.rs @@ -39,7 +39,7 @@ impl super::Extension for MultiEchoExtension { params: &Mapping, _: &Vec, _: &HashMap, - ) -> Option { + ) -> super::ExtensionOut { let mut output: HashMap = HashMap::new(); for (key, value) in params.iter() { if let Some(key) = key.as_str() { @@ -48,6 +48,6 @@ impl super::Extension for MultiEchoExtension { } } } - Some(ExtensionResult::Multiple(output)) + Ok(Some(ExtensionResult::Multiple(output))) } } diff --git a/src/extension/random.rs b/src/extension/random.rs index 3a13d3f..d464598 100644 --- a/src/extension/random.rs +++ b/src/extension/random.rs @@ -41,11 +41,11 @@ impl super::Extension for RandomExtension { params: &Mapping, args: &Vec, _: &HashMap, - ) -> Option { + ) -> super::ExtensionOut { let choices = params.get(&Value::from("choices")); if choices.is_none() { warn!("No 'choices' parameter specified for random variable"); - return None; + return Ok(None); } let choices = choices.unwrap().as_sequence(); if let Some(choices) = choices { @@ -62,17 +62,17 @@ impl super::Extension for RandomExtension { // Render arguments let output = crate::render::utils::render_args(output, args); - return Some(ExtensionResult::Single(output)); + return Ok(Some(ExtensionResult::Single(output))); } None => { error!("Could not select a random choice."); - return None; + return Err(super::ExtensionError::Internal) } } } error!("choices array have an invalid format '{:?}'", choices); - None + Err(super::ExtensionError::Internal) } } @@ -88,7 +88,7 @@ mod tests { params.insert(Value::from("choices"), Value::from(choices.clone())); let extension = RandomExtension::new(); - let output = extension.calculate(¶ms, &vec![], &HashMap::new()); + let output = extension.calculate(¶ms, &vec![], &HashMap::new()).unwrap(); assert!(output.is_some()); @@ -106,7 +106,7 @@ mod tests { params.insert(Value::from("choices"), Value::from(choices.clone())); let extension = RandomExtension::new(); - let output = extension.calculate(¶ms, &vec!["test".to_owned()], &HashMap::new()); + let output = extension.calculate(¶ms, &vec!["test".to_owned()], &HashMap::new()).unwrap(); assert!(output.is_some()); diff --git a/src/extension/script.rs b/src/extension/script.rs index 3a68a10..5dbedc9 100644 --- a/src/extension/script.rs +++ b/src/extension/script.rs @@ -42,11 +42,11 @@ impl super::Extension for ScriptExtension { params: &Mapping, user_args: &Vec, vars: &HashMap, - ) -> Option { + ) -> super::ExtensionOut { let args = params.get(&Value::from("args")); if args.is_none() { warn!("No 'args' parameter specified for script variable"); - return None; + return Err(super::ExtensionError::Internal); } let args = args.unwrap().as_sequence(); if let Some(args) = args { @@ -145,17 +145,17 @@ impl super::Extension for ScriptExtension { output_str = output_str.trim().to_owned() } - return Some(ExtensionResult::Single(output_str)); + return Ok(Some(ExtensionResult::Single(output_str))); } Err(e) => { error!("Could not execute script '{:?}', error: {}", args, e); - return None; + return Err(super::ExtensionError::Internal); } } } error!("Could not execute script with args '{:?}'", args); - None + Err(super::ExtensionError::Internal) } } diff --git a/src/extension/shell.rs b/src/extension/shell.rs index 4e323a8..d3e1ed3 100644 --- a/src/extension/shell.rs +++ b/src/extension/shell.rs @@ -164,11 +164,11 @@ impl super::Extension for ShellExtension { params: &Mapping, args: &Vec, vars: &HashMap, - ) -> Option { + ) -> super::ExtensionOut { let cmd = params.get(&Value::from("cmd")); if cmd.is_none() { warn!("No 'cmd' parameter specified for shell variable"); - return None; + return Err(super::ExtensionError::Internal); } let inject_args = params @@ -186,7 +186,7 @@ impl super::Extension for ShellExtension { if shell.is_none() { error!("Invalid shell parameter, please select a valid one."); - return None; + return Err(super::ExtensionError::Internal); } shell.unwrap() @@ -257,11 +257,11 @@ impl super::Extension for ShellExtension { output_str = output_str.trim().to_owned() } - Some(ExtensionResult::Single(output_str)) + Ok(Some(ExtensionResult::Single(output_str))) } Err(e) => { error!("Could not execute cmd '{}', error: {}", cmd, e); - None + Err(super::ExtensionError::Internal) } } } @@ -279,7 +279,7 @@ mod tests { params.insert(Value::from("trim"), Value::from(false)); let extension = ShellExtension::new(); - let output = extension.calculate(¶ms, &vec![], &HashMap::new()); + let output = extension.calculate(¶ms, &vec![], &HashMap::new()).unwrap(); assert!(output.is_some()); @@ -302,7 +302,7 @@ mod tests { params.insert(Value::from("cmd"), Value::from("echo \"hello world\"")); let extension = ShellExtension::new(); - let output = extension.calculate(¶ms, &vec![], &HashMap::new()); + let output = extension.calculate(¶ms, &vec![], &HashMap::new()).unwrap(); assert!(output.is_some()); assert_eq!( @@ -320,7 +320,7 @@ mod tests { ); let extension = ShellExtension::new(); - let output = extension.calculate(¶ms, &vec![], &HashMap::new()); + let output = extension.calculate(¶ms, &vec![], &HashMap::new()).unwrap(); assert!(output.is_some()); assert_eq!( @@ -336,7 +336,7 @@ mod tests { params.insert(Value::from("trim"), Value::from("error")); let extension = ShellExtension::new(); - let output = extension.calculate(¶ms, &vec![], &HashMap::new()); + let output = extension.calculate(¶ms, &vec![], &HashMap::new()).unwrap(); assert!(output.is_some()); assert_eq!( @@ -353,7 +353,7 @@ mod tests { params.insert(Value::from("trim"), Value::from(true)); let extension = ShellExtension::new(); - let output = extension.calculate(¶ms, &vec![], &HashMap::new()); + let output = extension.calculate(¶ms, &vec![], &HashMap::new()).unwrap(); assert!(output.is_some()); assert_eq!( @@ -370,7 +370,7 @@ mod tests { params.insert(Value::from("inject_args"), Value::from(true)); let extension = ShellExtension::new(); - let output = extension.calculate(¶ms, &vec!["hello".to_owned()], &HashMap::new()); + let output = extension.calculate(¶ms, &vec!["hello".to_owned()], &HashMap::new()).unwrap(); assert!(output.is_some()); @@ -384,7 +384,7 @@ mod tests { params.insert(Value::from("cmd"), Value::from("echo 'hey friend' | awk '{ print $2 }'")); let extension = ShellExtension::new(); - let output = extension.calculate(¶ms, &vec!["hello".to_owned()], &HashMap::new()); + let output = extension.calculate(¶ms, &vec!["hello".to_owned()], &HashMap::new()).unwrap(); assert!(output.is_some()); @@ -399,7 +399,7 @@ mod tests { params.insert(Value::from("inject_args"), Value::from(true)); let extension = ShellExtension::new(); - let output = extension.calculate(¶ms, &vec!["hello".to_owned()], &HashMap::new()); + let output = extension.calculate(¶ms, &vec!["hello".to_owned()], &HashMap::new()).unwrap(); assert!(output.is_some()); @@ -422,7 +422,7 @@ mod tests { "var1".to_owned(), ExtensionResult::Single("hello".to_owned()), ); - let output = extension.calculate(¶ms, &vec![], &vars); + let output = extension.calculate(¶ms, &vec![], &vars).unwrap(); assert!(output.is_some()); assert_eq!(output.unwrap(), ExtensionResult::Single("hello".to_owned())); @@ -443,7 +443,7 @@ mod tests { let mut subvars = HashMap::new(); subvars.insert("name".to_owned(), "John".to_owned()); vars.insert("form1".to_owned(), ExtensionResult::Multiple(subvars)); - let output = extension.calculate(¶ms, &vec![], &vars); + let output = extension.calculate(¶ms, &vec![], &vars).unwrap(); assert!(output.is_some()); assert_eq!(output.unwrap(), ExtensionResult::Single("John".to_owned())); diff --git a/src/extension/vardummy.rs b/src/extension/vardummy.rs index 4108ae1..d683267 100644 --- a/src/extension/vardummy.rs +++ b/src/extension/vardummy.rs @@ -39,14 +39,14 @@ impl super::Extension for VarDummyExtension { params: &Mapping, _: &Vec, vars: &HashMap, - ) -> Option { + ) -> super::ExtensionOut { let target = params.get(&Value::from("target")); if let Some(target) = target { let value = vars.get(target.as_str().unwrap_or_default()); - Some(value.unwrap().clone()) + Ok(Some(value.unwrap().clone())) } else { - None + Ok(None) } } } diff --git a/src/render/default.rs b/src/render/default.rs index 3415f0d..01b31aa 100644 --- a/src/render/default.rs +++ b/src/render/default.rs @@ -182,20 +182,28 @@ impl super::Renderer for DefaultRenderer { // Normal extension variables let extension = self.extension_map.get(&variable.var_type); if let Some(extension) = extension { - let ext_out = + let ext_res = extension.calculate(&variable.params, &args, &output_map); - if let Some(output) = ext_out { - output_map.insert(variable.name.clone(), output); - } else { - output_map.insert( - variable.name.clone(), - ExtensionResult::Single("".to_owned()), - ); - warn!( - "Could not generate output for variable: {}", - variable.name - ); + match ext_res { + Ok(ext_out) => { + if let Some(output) = ext_out { + output_map.insert(variable.name.clone(), output); + } else { + output_map.insert( + variable.name.clone(), + ExtensionResult::Single("".to_owned()), + ); + warn!( + "Could not generate output for variable: {}", + variable.name + ); + } + } + Err(_) => { + return RenderResult::Error + } } + } else { error!( "No extension found for variable type: {}",