rash_core 2.18.1

Declarative shell scripting using Rust native bindings
Documentation
/// ANCHOR: module
/// # assert
///
/// Assert given expressions are true.
///
/// ## Attributes
///
/// ```yaml
/// check_mode:
///   support: always
/// ```
/// ANCHOR_END: module
/// ANCHOR: examples
/// ## Example
///
/// ```yaml
/// - assert:
///     that:
///       - boo is defined
///       - 1 + 1 == 2
///       - env.MY_VAR is defined
/// ```
/// ANCHOR_END: examples
use crate::context::GlobalParams;
use crate::error::{Error, ErrorKind, Result};
use crate::jinja::is_render_string;
use crate::modules::{Module, ModuleResult, parse_params};

#[cfg(feature = "docs")]
use rash_derive::DocJsonSchema;

use minijinja::Value;
#[cfg(feature = "docs")]
use schemars::{JsonSchema, Schema};
use serde::Deserialize;
use serde_norway::Value as YamlValue;

#[derive(Debug, PartialEq, Deserialize)]
#[cfg_attr(feature = "docs", derive(JsonSchema, DocJsonSchema))]
#[serde(deny_unknown_fields)]
pub struct Params {
    /// A list of string expressions of the same form that can be passed to the
    /// _when_ statement.
    that: Vec<String>,
}

fn verify_conditions(params: Params, vars: &Value) -> Result<ModuleResult> {
    params.that.iter().try_for_each(|expression| {
        if is_render_string(expression, vars)? {
            Ok(())
        } else {
            Err(Error::new(
                ErrorKind::Other,
                format!("{} expression is false", &expression),
            ))
        }
    })?;
    Ok(ModuleResult {
        changed: false,
        output: None,
        extra: None,
    })
}

#[derive(Debug)]
pub struct Assert;

impl Module for Assert {
    fn get_name(&self) -> &str {
        "assert"
    }

    fn exec(
        &self,
        _: &GlobalParams,
        optional_params: YamlValue,
        vars: &Value,
        _check_mode: bool,
    ) -> Result<(ModuleResult, Option<Value>)> {
        Ok((
            verify_conditions(parse_params(optional_params)?, vars)?,
            None,
        ))
    }

    #[cfg(feature = "docs")]
    fn get_json_schema(&self) -> Option<Schema> {
        Some(Params::get_json_schema())
    }
}

#[cfg(test)]
mod tests {
    use minijinja::context;

    use super::*;

    #[test]
    fn test_parse_params() {
        let yaml: YamlValue = serde_norway::from_str(
            r#"
            that:
              - 1 == 1
            "#,
        )
        .unwrap();
        let params: Params = parse_params(yaml).unwrap();
        assert_eq!(
            params,
            Params {
                that: vec!["1 == 1".to_owned()],
            }
        );
    }

    #[test]
    fn test_parse_params_random_field() {
        let yaml: YamlValue = serde_norway::from_str(
            r#"
            that:
              - 1 == 1
            yea: boo
            "#,
        )
        .unwrap();
        let error = parse_params::<Params>(yaml).unwrap_err();
        assert_eq!(error.kind(), ErrorKind::InvalidData);
    }

    #[test]
    fn test_verify_conditions() {
        let _ = verify_conditions(
            Params {
                that: vec!["1 == 1".to_owned()],
            },
            &context! {},
        )
        .unwrap();
    }

    #[test]
    fn test_verify_conditions_fail() {
        let _ = verify_conditions(
            Params {
                that: vec!["1 != 1".to_owned()],
            },
            &context! {},
        )
        .unwrap_err();
    }
}