feat(render): create FormExtension
This commit is contained in:
parent
d7ebd2a4dd
commit
a2522af57a
120
espanso-render/src/extension/form.rs
Normal file
120
espanso-render/src/extension/form.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* This file is part of espanso.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019-2021 Federico Terzi
|
||||||
|
*
|
||||||
|
* espanso is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* espanso is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::renderer::VAR_REGEX;
|
||||||
|
use log::error;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
renderer::render_variables, Extension, ExtensionOutput, ExtensionResult, Params, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref EMPTY_PARAMS: Params = Params::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FormProvider {
|
||||||
|
fn show(&self, layout: &str, fields: &Params, options: &Params) -> FormProviderResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum FormProviderResult {
|
||||||
|
Success(HashMap<String, String>),
|
||||||
|
Aborted,
|
||||||
|
Error(anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FormExtension<'a> {
|
||||||
|
provider: &'a dyn FormProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
impl<'a> FormExtension<'a> {
|
||||||
|
pub fn new(provider: &'a dyn FormProvider) -> Self {
|
||||||
|
Self { provider }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Extension for FormExtension<'a> {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"form"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate(
|
||||||
|
&self,
|
||||||
|
_: &crate::Context,
|
||||||
|
scope: &crate::Scope,
|
||||||
|
params: &Params,
|
||||||
|
) -> crate::ExtensionResult {
|
||||||
|
let layout = if let Some(Value::String(layout)) = params.get("layout") {
|
||||||
|
layout
|
||||||
|
} else {
|
||||||
|
return crate::ExtensionResult::Error(FormExtensionError::MissingLayout.into());
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fields = if let Some(Value::Object(fields)) = params.get("fields") {
|
||||||
|
fields.clone()
|
||||||
|
} else {
|
||||||
|
Params::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Inject scope variables into fields (if needed)
|
||||||
|
inject_scope(&mut fields, scope);
|
||||||
|
|
||||||
|
match self.provider.show(layout, &fields, &EMPTY_PARAMS) {
|
||||||
|
FormProviderResult::Success(values) => {
|
||||||
|
ExtensionResult::Success(ExtensionOutput::Multiple(values))
|
||||||
|
}
|
||||||
|
FormProviderResult::Aborted => ExtensionResult::Aborted,
|
||||||
|
FormProviderResult::Error(error) => ExtensionResult::Error(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
fn inject_scope(fields: &mut HashMap<String, Value>, scope: &HashMap<&str, ExtensionOutput>) -> () {
|
||||||
|
for (_, value) in fields {
|
||||||
|
if let Value::Object(field_options) = value {
|
||||||
|
if let Some(Value::String(default_value)) = field_options.get_mut("default") {
|
||||||
|
if VAR_REGEX.is_match(default_value) {
|
||||||
|
match render_variables(&default_value, scope) {
|
||||||
|
Ok(rendered) => *default_value = rendered,
|
||||||
|
Err(err) => error!(
|
||||||
|
"error while injecting variable in form default value: {}",
|
||||||
|
err
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum FormExtensionError {
|
||||||
|
#[error("missing layout parameter")]
|
||||||
|
MissingLayout,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
}
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
pub mod date;
|
pub mod date;
|
||||||
pub mod echo;
|
pub mod echo;
|
||||||
|
pub mod form;
|
||||||
pub mod clipboard;
|
pub mod clipboard;
|
||||||
pub mod shell;
|
pub mod shell;
|
||||||
pub mod script;
|
pub mod script;
|
||||||
|
|
|
@ -21,8 +21,9 @@ use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CasingStyle, Context, Extension, ExtensionOutput, ExtensionResult, RenderOptions, RenderResult,
|
CasingStyle, Context, Extension, ExtensionOutput, ExtensionResult, RenderOptions, RenderResult,
|
||||||
Renderer, Scope, Template, Value, Variable
|
Renderer, Scope, Template, Value, Variable,
|
||||||
};
|
};
|
||||||
|
use anyhow::Result;
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use regex::{Captures, Regex};
|
use regex::{Captures, Regex};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -40,7 +41,7 @@ pub(crate) struct DefaultRenderer<'a> {
|
||||||
extensions: HashMap<String, &'a dyn Extension>,
|
extensions: HashMap<String, &'a dyn Extension>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> DefaultRenderer<'a> {
|
impl<'a> DefaultRenderer<'a> {
|
||||||
pub fn new(extensions: Vec<&'a dyn Extension>) -> Self {
|
pub fn new(extensions: Vec<&'a dyn Extension>) -> Self {
|
||||||
let extensions = extensions
|
let extensions = extensions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -50,7 +51,7 @@ impl <'a> DefaultRenderer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> Renderer for DefaultRenderer<'a> {
|
impl<'a> Renderer for DefaultRenderer<'a> {
|
||||||
fn render(
|
fn render(
|
||||||
&self,
|
&self,
|
||||||
template: &Template,
|
template: &Template,
|
||||||
|
@ -150,9 +151,52 @@ impl <'a> Renderer for DefaultRenderer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the variables
|
// Replace the variables
|
||||||
|
match render_variables(&template.body, &scope) {
|
||||||
|
Ok(output) => output,
|
||||||
|
Err(error) => {
|
||||||
|
return RenderResult::Error(error.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
template.body.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Process the casing style
|
||||||
|
let body_with_casing = match options.casing_style {
|
||||||
|
CasingStyle::None => body,
|
||||||
|
CasingStyle::Uppercase => body.to_uppercase(),
|
||||||
|
CasingStyle::Capitalize => {
|
||||||
|
// Capitalize the first letter
|
||||||
|
let mut v: Vec<char> = body.chars().collect();
|
||||||
|
v[0] = v[0].to_uppercase().next().unwrap();
|
||||||
|
v.into_iter().collect()
|
||||||
|
}
|
||||||
|
CasingStyle::CapitalizeWords => {
|
||||||
|
// Capitalize the first letter of each word
|
||||||
|
WORD_REGEX
|
||||||
|
.replace_all(&body, |caps: &Captures| {
|
||||||
|
if let Some(word_match) = caps.get(0) {
|
||||||
|
let mut v: Vec<char> = word_match.as_str().chars().collect();
|
||||||
|
v[0] = v[0].to_uppercase().next().unwrap();
|
||||||
|
let capitalized_word: String = v.into_iter().collect();
|
||||||
|
capitalized_word
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RenderResult::Success(body_with_casing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
pub(crate) fn render_variables(body: &str, scope: &Scope) -> Result<String> {
|
||||||
let mut replacing_error = None;
|
let mut replacing_error = None;
|
||||||
let output = VAR_REGEX
|
let output = VAR_REGEX
|
||||||
.replace_all(&template.body, |caps: &Captures| {
|
.replace_all(&body, |caps: &Captures| {
|
||||||
let var_name = caps.name("name").unwrap().as_str();
|
let var_name = caps.name("name").unwrap().as_str();
|
||||||
let var_subname = caps.name("subname");
|
let var_subname = caps.name("subname");
|
||||||
match scope.get(var_name) {
|
match scope.get(var_name) {
|
||||||
|
@ -168,55 +212,30 @@ impl <'a> Renderer for DefaultRenderer<'a> {
|
||||||
"nested name missing from multi-value variable: {}",
|
"nested name missing from multi-value variable: {}",
|
||||||
var_name
|
var_name
|
||||||
);
|
);
|
||||||
replacing_error = Some(RendererError::MissingVariable(format!("nested name missing from multi-value variable: {}", var_name)));
|
replacing_error = Some(RendererError::MissingVariable(format!(
|
||||||
|
"nested name missing from multi-value variable: {}",
|
||||||
|
var_name
|
||||||
|
)));
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
replacing_error = Some(RendererError::MissingVariable(format!("variable {} is missing", var_name)));
|
replacing_error = Some(RendererError::MissingVariable(format!(
|
||||||
|
"variable {} is missing",
|
||||||
|
var_name
|
||||||
|
)));
|
||||||
""
|
""
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
if let Some(error) = replacing_error {
|
if let Some(error) = replacing_error {
|
||||||
return RenderResult::Error(error.into());
|
return Err(error.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
output
|
Ok(output)
|
||||||
} else {
|
|
||||||
template.body.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Process the casing style
|
|
||||||
let body_with_casing = match options.casing_style {
|
|
||||||
CasingStyle::None => body,
|
|
||||||
CasingStyle::Uppercase => body.to_uppercase(),
|
|
||||||
CasingStyle::Capitalize => {
|
|
||||||
// Capitalize the first letter
|
|
||||||
let mut v: Vec<char> = body.chars().collect();
|
|
||||||
v[0] = v[0].to_uppercase().next().unwrap();
|
|
||||||
v.into_iter().collect()
|
|
||||||
},
|
|
||||||
CasingStyle::CapitalizeWords => {
|
|
||||||
// Capitalize the first letter of each word
|
|
||||||
WORD_REGEX.replace_all(&body, |caps: &Captures| {
|
|
||||||
if let Some(word_match) = caps.get(0) {
|
|
||||||
let mut v: Vec<char> = word_match.as_str().chars().collect();
|
|
||||||
v[0] = v[0].to_uppercase().next().unwrap();
|
|
||||||
let capitalized_word: String = v.into_iter().collect();
|
|
||||||
capitalized_word
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
}
|
|
||||||
}).to_string()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
RenderResult::Success(body_with_casing)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_matching_template<'a>(
|
fn get_matching_template<'a>(
|
||||||
|
@ -280,7 +299,7 @@ mod tests {
|
||||||
return ExtensionResult::Aborted;
|
return ExtensionResult::Aborted;
|
||||||
}
|
}
|
||||||
if params.get("error").is_some() {
|
if params.get("error").is_some() {
|
||||||
return ExtensionResult::Error(RendererError::MissingVariable("missing".to_string()).into())
|
return ExtensionResult::Error(RendererError::MissingVariable("missing".to_string()).into());
|
||||||
}
|
}
|
||||||
ExtensionResult::Aborted
|
ExtensionResult::Aborted
|
||||||
}
|
}
|
||||||
|
@ -299,13 +318,16 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn template(body: &str, vars: &[(&str, &str)]) -> Template {
|
pub fn template(body: &str, vars: &[(&str, &str)]) -> Template {
|
||||||
let vars = vars.iter().map(|(name, value)| {
|
let vars = vars
|
||||||
Variable {
|
.iter()
|
||||||
|
.map(|(name, value)| Variable {
|
||||||
name: (*name).to_string(),
|
name: (*name).to_string(),
|
||||||
var_type: "mock".to_string(),
|
var_type: "mock".to_string(),
|
||||||
params: Params::from_iter(vec![("echo".to_string(), Value::String((*value).to_string()))].into_iter())
|
params: Params::from_iter(
|
||||||
}
|
vec![("echo".to_string(), Value::String((*value).to_string()))].into_iter(),
|
||||||
}).collect();
|
),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
Template {
|
Template {
|
||||||
ids: vec!["id".to_string()],
|
ids: vec!["id".to_string()],
|
||||||
body: body.to_string(),
|
body: body.to_string(),
|
||||||
|
@ -316,34 +338,52 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn no_variable_no_styling() {
|
fn no_variable_no_styling() {
|
||||||
let renderer = get_renderer();
|
let renderer = get_renderer();
|
||||||
let res = renderer.render(&template_for_str("plain body"), &Default::default(), &Default::default());
|
let res = renderer.render(
|
||||||
|
&template_for_str("plain body"),
|
||||||
|
&Default::default(),
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
assert!(matches!(res, RenderResult::Success(str) if str == "plain body"));
|
assert!(matches!(res, RenderResult::Success(str) if str == "plain body"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_variable_capitalize() {
|
fn no_variable_capitalize() {
|
||||||
let renderer = get_renderer();
|
let renderer = get_renderer();
|
||||||
let res = renderer.render(&template_for_str("plain body"), &Default::default(), &RenderOptions {
|
let res = renderer.render(
|
||||||
|
&template_for_str("plain body"),
|
||||||
|
&Default::default(),
|
||||||
|
&RenderOptions {
|
||||||
casing_style: CasingStyle::Capitalize,
|
casing_style: CasingStyle::Capitalize,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
assert!(matches!(res, RenderResult::Success(str) if str == "Plain body"));
|
assert!(matches!(res, RenderResult::Success(str) if str == "Plain body"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_variable_capitalize_words() {
|
fn no_variable_capitalize_words() {
|
||||||
let renderer = get_renderer();
|
let renderer = get_renderer();
|
||||||
let res = renderer.render(&template_for_str("ordinary least squares, with other.punctuation !Marks"), &Default::default(), &RenderOptions {
|
let res = renderer.render(
|
||||||
|
&template_for_str("ordinary least squares, with other.punctuation !Marks"),
|
||||||
|
&Default::default(),
|
||||||
|
&RenderOptions {
|
||||||
casing_style: CasingStyle::CapitalizeWords,
|
casing_style: CasingStyle::CapitalizeWords,
|
||||||
});
|
},
|
||||||
assert!(matches!(res, RenderResult::Success(str) if str == "Ordinary Least Squares, With Other.Punctuation !Marks"));
|
);
|
||||||
|
assert!(
|
||||||
|
matches!(res, RenderResult::Success(str) if str == "Ordinary Least Squares, With Other.Punctuation !Marks")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_variable_uppercase() {
|
fn no_variable_uppercase() {
|
||||||
let renderer = get_renderer();
|
let renderer = get_renderer();
|
||||||
let res = renderer.render(&template_for_str("plain body"), &Default::default(), &RenderOptions {
|
let res = renderer.render(
|
||||||
|
&template_for_str("plain body"),
|
||||||
|
&Default::default(),
|
||||||
|
&RenderOptions {
|
||||||
casing_style: CasingStyle::Uppercase,
|
casing_style: CasingStyle::Uppercase,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
assert!(matches!(res, RenderResult::Success(str) if str == "PLAIN BODY"));
|
assert!(matches!(res, RenderResult::Success(str) if str == "PLAIN BODY"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,16 +407,21 @@ mod tests {
|
||||||
fn global_variable() {
|
fn global_variable() {
|
||||||
let renderer = get_renderer();
|
let renderer = get_renderer();
|
||||||
let template = template("hello {{var}}", &[]);
|
let template = template("hello {{var}}", &[]);
|
||||||
let res = renderer.render(&template, &Context {
|
let res = renderer.render(
|
||||||
global_vars: vec![
|
&template,
|
||||||
&Variable {
|
&Context {
|
||||||
|
global_vars: vec![&Variable {
|
||||||
name: "var".to_string(),
|
name: "var".to_string(),
|
||||||
var_type: "mock".to_string(),
|
var_type: "mock".to_string(),
|
||||||
params: Params::from_iter(vec![("echo".to_string(), Value::String("world".to_string()))])
|
params: Params::from_iter(vec![(
|
||||||
}
|
"echo".to_string(),
|
||||||
],
|
Value::String("world".to_string()),
|
||||||
|
)]),
|
||||||
|
}],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}, &Default::default());
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
assert!(matches!(res, RenderResult::Success(str) if str == "hello world"));
|
assert!(matches!(res, RenderResult::Success(str) if str == "hello world"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,26 +434,33 @@ mod tests {
|
||||||
Variable {
|
Variable {
|
||||||
name: "local".to_string(),
|
name: "local".to_string(),
|
||||||
var_type: "mock".to_string(),
|
var_type: "mock".to_string(),
|
||||||
params: Params::from_iter(vec![("echo".to_string(), Value::String("Bob".to_string()))].into_iter())
|
params: Params::from_iter(
|
||||||
|
vec![("echo".to_string(), Value::String("Bob".to_string()))].into_iter(),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
Variable {
|
Variable {
|
||||||
name: "var".to_string(),
|
name: "var".to_string(),
|
||||||
var_type: "global".to_string(),
|
var_type: "global".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let res = renderer.render(&template, &Context {
|
let res = renderer.render(
|
||||||
global_vars: vec![
|
&template,
|
||||||
&Variable {
|
&Context {
|
||||||
|
global_vars: vec![&Variable {
|
||||||
name: "var".to_string(),
|
name: "var".to_string(),
|
||||||
var_type: "mock".to_string(),
|
var_type: "mock".to_string(),
|
||||||
params: Params::from_iter(vec![("read".to_string(), Value::String("local".to_string()))])
|
params: Params::from_iter(vec![(
|
||||||
}
|
"read".to_string(),
|
||||||
],
|
Value::String("local".to_string()),
|
||||||
|
)]),
|
||||||
|
}],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}, &Default::default());
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
assert!(matches!(res, RenderResult::Success(str) if str == "hello Bob Bob"));
|
assert!(matches!(res, RenderResult::Success(str) if str == "hello Bob Bob"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,13 +469,13 @@ mod tests {
|
||||||
let renderer = get_renderer();
|
let renderer = get_renderer();
|
||||||
let template = Template {
|
let template = Template {
|
||||||
body: "hello {{var}}".to_string(),
|
body: "hello {{var}}".to_string(),
|
||||||
vars: vec![
|
vars: vec![Variable {
|
||||||
Variable {
|
|
||||||
name: "var".to_string(),
|
name: "var".to_string(),
|
||||||
var_type: "match".to_string(),
|
var_type: "match".to_string(),
|
||||||
params: Params::from_iter(vec![("trigger".to_string(), Value::String("nested".to_string()))].into_iter())
|
params: Params::from_iter(
|
||||||
},
|
vec![("trigger".to_string(), Value::String("nested".to_string()))].into_iter(),
|
||||||
],
|
),
|
||||||
|
}],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let nested_template = Template {
|
let nested_template = Template {
|
||||||
|
@ -431,10 +483,14 @@ mod tests {
|
||||||
body: "world".to_string(),
|
body: "world".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let res = renderer.render(&template, &Context {
|
let res = renderer.render(
|
||||||
|
&template,
|
||||||
|
&Context {
|
||||||
templates: vec![&nested_template],
|
templates: vec![&nested_template],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}, &Default::default());
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
assert!(matches!(res, RenderResult::Success(str) if str == "hello world"));
|
assert!(matches!(res, RenderResult::Success(str) if str == "hello world"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,18 +499,22 @@ mod tests {
|
||||||
let renderer = get_renderer();
|
let renderer = get_renderer();
|
||||||
let template = Template {
|
let template = Template {
|
||||||
body: "hello {{var}}".to_string(),
|
body: "hello {{var}}".to_string(),
|
||||||
vars: vec![
|
vars: vec![Variable {
|
||||||
Variable {
|
|
||||||
name: "var".to_string(),
|
name: "var".to_string(),
|
||||||
var_type: "match".to_string(),
|
var_type: "match".to_string(),
|
||||||
params: Params::from_iter(vec![("trigger".to_string(), Value::String("nested".to_string()))].into_iter())
|
params: Params::from_iter(
|
||||||
},
|
vec![("trigger".to_string(), Value::String("nested".to_string()))].into_iter(),
|
||||||
],
|
),
|
||||||
|
}],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let res = renderer.render(&template, &Context {
|
let res = renderer.render(
|
||||||
|
&template,
|
||||||
|
&Context {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}, &Default::default());
|
},
|
||||||
|
&Default::default(),
|
||||||
|
);
|
||||||
assert!(matches!(res, RenderResult::Error(_)));
|
assert!(matches!(res, RenderResult::Error(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,13 +523,11 @@ mod tests {
|
||||||
let renderer = get_renderer();
|
let renderer = get_renderer();
|
||||||
let template = Template {
|
let template = Template {
|
||||||
body: "hello {{var}}".to_string(),
|
body: "hello {{var}}".to_string(),
|
||||||
vars: vec![
|
vars: vec![Variable {
|
||||||
Variable {
|
|
||||||
name: "var".to_string(),
|
name: "var".to_string(),
|
||||||
var_type: "mock".to_string(),
|
var_type: "mock".to_string(),
|
||||||
params: Params::from_iter(vec![("abort".to_string(), Value::Null)].into_iter()),
|
params: Params::from_iter(vec![("abort".to_string(), Value::Null)].into_iter()),
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let res = renderer.render(&template, &Default::default(), &Default::default());
|
let res = renderer.render(&template, &Default::default(), &Default::default());
|
||||||
|
@ -481,13 +539,11 @@ mod tests {
|
||||||
let renderer = get_renderer();
|
let renderer = get_renderer();
|
||||||
let template = Template {
|
let template = Template {
|
||||||
body: "hello {{var}}".to_string(),
|
body: "hello {{var}}".to_string(),
|
||||||
vars: vec![
|
vars: vec![Variable {
|
||||||
Variable {
|
|
||||||
name: "var".to_string(),
|
name: "var".to_string(),
|
||||||
var_type: "mock".to_string(),
|
var_type: "mock".to_string(),
|
||||||
params: Params::from_iter(vec![("error".to_string(), Value::Null)].into_iter()),
|
params: Params::from_iter(vec![("error".to_string(), Value::Null)].into_iter()),
|
||||||
}
|
}],
|
||||||
],
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let res = renderer.render(&template, &Default::default(), &Default::default());
|
let res = renderer.render(&template, &Default::default(), &Default::default());
|
||||||
|
|
Loading…
Reference in New Issue
Block a user