Skip to content

Commit

Permalink
fix(task): Render params recursivey and respect omit
Browse files Browse the repository at this point in the history
Signed-off-by: Alexander Gil <[email protected]>
  • Loading branch information
pando85 committed Aug 15, 2024
1 parent 41283d0 commit 8709475
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 55 deletions.
99 changes: 79 additions & 20 deletions rash_core/src/jinja/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,51 @@ fn init_env() -> Environment<'static> {
static MINIJINJA_ENV: LazyLock<Environment<'static>> = LazyLock::new(init_env);

#[inline(always)]
pub fn render(value: YamlValue, vars: &Value) -> Result<YamlValue> {
match value.clone() {
YamlValue::String(s) => Ok(serde_yaml::from_str(&render_string(&s, vars)?)?),
YamlValue::Number(_) => Ok(value),
YamlValue::Bool(_) => Ok(value),
YamlValue::Sequence(v) => Ok(YamlValue::Sequence(
v.iter()
.map(|x| render(x.clone(), vars))
.collect::<Result<Vec<_>>>()?,
)),
YamlValue::Mapping(x) => {
let mut rendered_map = serde_yaml::Mapping::new();
let mut current_vars = vars.clone();
pub fn render_map(map: serde_yaml::Mapping, vars: &Value, force_string: bool) -> Result<YamlValue> {
let mut rendered_map = serde_yaml::Mapping::new();
let mut current_vars = vars.clone();

for (k, v) in x.iter() {
let rendered_value = render(v.clone(), &current_vars)?;
for (k, v) in map.iter() {
match render(v.clone(), &current_vars, force_string) {
Ok(v) => {
current_vars = context! {
..current_vars,
..Value::from_serialize(
json!({
// safe unwrap: k is always a String
k.as_str().unwrap(): rendered_value.clone()
k.as_str().unwrap(): v.clone()
})
)
};
rendered_map.insert(k.clone(), rendered_value);
rendered_map.insert(k.clone(), v);
}
Err(e) if e.kind() == ErrorKind::OmitParam => (),
Err(e) => Err(e)?,
}
}

Ok(YamlValue::Mapping(rendered_map))
Ok(YamlValue::Mapping(rendered_map))
}

#[inline(always)]
pub fn render(value: YamlValue, vars: &Value, force_string: bool) -> Result<YamlValue> {
match value.clone() {
YamlValue::String(s) => {
let rendered = &render_string(&s, vars)?;
if force_string {
Ok(YamlValue::String(rendered.to_string()))
} else {
Ok(serde_yaml::from_str(rendered)?)
}
}
YamlValue::Number(_) => Ok(value),
YamlValue::Bool(_) => Ok(value),
YamlValue::Sequence(v) => Ok(YamlValue::Sequence(
v.iter()
.map(|x| render(x.clone(), vars, force_string))
.collect::<Result<Vec<_>>>()?,
)),
YamlValue::Mapping(x) => render_map(x, vars, force_string),
_ => Err(Error::new(
ErrorKind::InvalidData,
format!("{value:?} is not a valid render value"),
Expand Down Expand Up @@ -95,12 +110,56 @@ pub fn is_render_string(s: &str, vars: &Value) -> Result<bool> {
mod tests {
use super::*;

#[test]
fn test_render_map() {
let yaml: YamlValue = serde_yaml::from_str(
r#"
yea: "{{ boo }}"
"#,
)
.unwrap();
let r_yaml = render_map(
yaml.as_mapping().unwrap().to_owned(),
&context! {boo => 1},
false,
)
.unwrap();
let expected: YamlValue = serde_yaml::from_str(
r#"
yea: 1
"#,
)
.unwrap();
assert_eq!(r_yaml, expected);

let yaml: YamlValue = serde_yaml::from_str(
r#"
yea: "{{ boo }}"
fuu: "{{ zoo | default(omit) }}"
"#,
)
.unwrap();
let r_yaml = render_map(
yaml.as_mapping().unwrap().to_owned(),
&context! {boo => 2},
false,
)
.unwrap();
let expected: YamlValue = serde_yaml::from_str(
r#"
yea: 2
"#,
)
.unwrap();
assert_eq!(r_yaml, expected);
}

#[test]
fn test_render() {
let r_yaml = render(YamlValue::from(1), &context! {}).unwrap();
let r_yaml = render(YamlValue::from(1), &context! {}, false).unwrap();
assert_eq!(r_yaml, YamlValue::from(1));

let r_yaml = render(YamlValue::from("yea"), &context! {}).unwrap();
let r_yaml = render(YamlValue::from("yea"), &context! {}, false).unwrap();
assert_eq!(r_yaml, YamlValue::from("yea"));
}

Expand Down
6 changes: 4 additions & 2 deletions rash_core/src/modules/set_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
/// ```
/// ANCHOR_END: examples
use crate::error::{Error, ErrorKind, Result};
use crate::jinja::render;
use crate::modules::{Module, ModuleResult};

use minijinja::Value;

use minijinja::context;
Expand All @@ -60,7 +62,7 @@ impl Module for SetVars {
vars: Value,
_check_mode: bool,
) -> Result<(ModuleResult, Value)> {
let mut new_vars = vars;
let mut new_vars = vars.clone();

match params {
YamlValue::Mapping(map) => {
Expand All @@ -72,7 +74,7 @@ impl Module for SetVars {
format!("{:?} is not a valid string", &hash_map.0),
)
})?;
let element = json!({key: hash_map.1});
let element = json!({key: render(hash_map.1.clone(), &vars, false)?});
new_vars = context! {..Value::from_serialize(element), ..new_vars.clone()};
Ok(())
})
Expand Down
39 changes: 6 additions & 33 deletions rash_core/src/task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@ mod new;
mod valid;

use crate::error::{Error, ErrorKind, Result};
use crate::jinja::{is_render_string, render, render_string};
use crate::jinja::{is_render_string, render, render_map, render_string};
use crate::modules::{Module, ModuleResult};
use crate::task::new::TaskNew;
use minijinja::Value;

use rash_derive::FieldNames;

use std::process::exit;
use std::result::Result as StdResult;

use ipc_channel::ipc::IpcReceiver;
use ipc_channel::ipc::{self, IpcSender};
use minijinja::context;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use minijinja::{context, Value};
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::{fork, setgid, setuid, ForkResult, Uid, User};
use serde_error::Error as SerdeError;
Expand Down Expand Up @@ -113,7 +111,7 @@ impl Task {
match self.vars.clone() {
Some(v) => {
trace!("extend vars: {:?}", &v);
let rendered_value = match render(v.clone(), &additional_vars) {
let rendered_value = match render(v.clone(), &additional_vars, false) {
Ok(v) => Value::from_serialize(v),
Err(e) if e.kind() == ErrorKind::OmitParam => context! {},
Err(e) => return Err(e),
Expand All @@ -129,32 +127,7 @@ impl Task {

let original_params = self.params.clone();
match original_params {
YamlValue::Mapping(map) => match map
.iter()
.filter_map(|t| match t.1 {
YamlValue::String(s) => match render_string(s, &extended_vars) {
Ok(s) => Some(Ok((t.0.clone(), YamlValue::String(s)))),
Err(e) if e.kind() == ErrorKind::OmitParam => None,
Err(e) => Some(Err(e)),
},
YamlValue::Sequence(x) => match x
.iter()
.map(|value| render(value.clone(), &extended_vars))
.collect::<Result<Vec<YamlValue>>>()
{
Ok(rendered_vec) => {
Some(Ok((t.0.clone(), YamlValue::Sequence(rendered_vec))))
}
Err(e) => Some(Err(e)),
},
_ => Some(Ok((t.0.clone(), t.1.clone()))),
})
.collect::<Result<_>>()
{
Ok(map) => Ok(YamlValue::Mapping(map)),
Err(e) => Err(e),
},

YamlValue::Mapping(x) => render_map(x.clone(), &extended_vars, true),
YamlValue::String(s) => Ok(YamlValue::String(render_string(&s, &extended_vars)?)),
_ => Err(Error::new(
ErrorKind::InvalidData,
Expand All @@ -178,7 +151,7 @@ impl Task {
match value.as_sequence() {
Some(v) => Ok(v
.iter()
.map(|item| render(item.clone(), &vars))
.map(|item| render(item.clone(), &vars, true))
.collect::<Result<Vec<YamlValue>>>()?),
None => Err(Error::new(ErrorKind::NotFound, "loop is not iterable")),
}
Expand Down

0 comments on commit 8709475

Please sign in to comment.