Refactor extensions to allow them to stop the expansion process. Fix #475
This commit is contained in:
parent
c5c2a4ab90
commit
cc72f10398
|
@ -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<dyn ClipboardManager>,
|
||||
}
|
||||
|
@ -42,11 +44,11 @@ impl super::Extension for ClipboardExtension {
|
|||
_: &Mapping,
|
||||
_: &Vec<String>,
|
||||
_: &HashMap<String, ExtensionResult>,
|
||||
) -> Option<ExtensionResult> {
|
||||
) -> ExtensionOut {
|
||||
if let Some(clipboard) = self.clipboard_manager.get_clipboard() {
|
||||
Some(ExtensionResult::Single(clipboard))
|
||||
Ok(Some(ExtensionResult::Single(clipboard)))
|
||||
} else {
|
||||
None
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String>,
|
||||
_: &HashMap<String, ExtensionResult>,
|
||||
) -> Option<ExtensionResult> {
|
||||
) -> ExtensionOut {
|
||||
let mut now: DateTime<Local> = 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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,15 +43,15 @@ impl super::Extension for DummyExtension {
|
|||
params: &Mapping,
|
||||
_: &Vec<String>,
|
||||
_: &HashMap<String, ExtensionResult>,
|
||||
) -> Option<ExtensionResult> {
|
||||
) -> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,13 +43,13 @@ impl super::Extension for FormExtension {
|
|||
params: &Mapping,
|
||||
_: &Vec<String>,
|
||||
_: &HashMap<String, ExtensionResult>,
|
||||
) -> Option<ExtensionResult> {
|
||||
) -> 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<HashMap<String, String>, _> = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,17 @@ pub enum ExtensionResult {
|
|||
Multiple(HashMap<String, String>),
|
||||
}
|
||||
|
||||
#[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<Option<ExtensionResult>, ExtensionError>;
|
||||
|
||||
pub trait Extension {
|
||||
fn name(&self) -> String;
|
||||
fn calculate(
|
||||
|
@ -45,7 +56,7 @@ pub trait Extension {
|
|||
params: &Mapping,
|
||||
args: &Vec<String>,
|
||||
current_vars: &HashMap<String, ExtensionResult>,
|
||||
) -> Option<ExtensionResult>;
|
||||
) -> ExtensionOut;
|
||||
}
|
||||
|
||||
pub fn get_extensions(
|
||||
|
|
|
@ -39,7 +39,7 @@ impl super::Extension for MultiEchoExtension {
|
|||
params: &Mapping,
|
||||
_: &Vec<String>,
|
||||
_: &HashMap<String, ExtensionResult>,
|
||||
) -> Option<ExtensionResult> {
|
||||
) -> super::ExtensionOut {
|
||||
let mut output: HashMap<String, String> = 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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,11 +41,11 @@ impl super::Extension for RandomExtension {
|
|||
params: &Mapping,
|
||||
args: &Vec<String>,
|
||||
_: &HashMap<String, ExtensionResult>,
|
||||
) -> Option<ExtensionResult> {
|
||||
) -> 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());
|
||||
|
||||
|
|
|
@ -42,11 +42,11 @@ impl super::Extension for ScriptExtension {
|
|||
params: &Mapping,
|
||||
user_args: &Vec<String>,
|
||||
vars: &HashMap<String, ExtensionResult>,
|
||||
) -> Option<ExtensionResult> {
|
||||
) -> 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -164,11 +164,11 @@ impl super::Extension for ShellExtension {
|
|||
params: &Mapping,
|
||||
args: &Vec<String>,
|
||||
vars: &HashMap<String, ExtensionResult>,
|
||||
) -> Option<ExtensionResult> {
|
||||
) -> 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()));
|
||||
|
|
|
@ -39,14 +39,14 @@ impl super::Extension for VarDummyExtension {
|
|||
params: &Mapping,
|
||||
_: &Vec<String>,
|
||||
vars: &HashMap<String, ExtensionResult>,
|
||||
) -> Option<ExtensionResult> {
|
||||
) -> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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: {}",
|
||||
|
|
Loading…
Reference in New Issue
Block a user