cfn-guard 3.2.0

AWS CloudFormation Guard is an open-source general-purpose policy-as-code evaluation tool. It provides developers with a simple-to-use, yet powerful and expressive domain-specific language (DSL) to define policies and enables developers to validate JSON- or YAML- formatted structured data with those policies.
Documentation
use crate::rules::values::CmpOperator;
use crate::rules::{path_value::PathAwareValue, EvaluationContext, EvaluationType, Result, Status};
use nom::lib::std::fmt::Formatter;
use serde::Serialize;

#[derive(Serialize, Debug)]
pub(crate) struct StatusContext {
    pub(crate) eval_type: EvaluationType,
    pub(crate) context: String,
    pub(crate) msg: Option<String>,
    pub(crate) from: Option<PathAwareValue>,
    pub(crate) to: Option<PathAwareValue>,
    pub(crate) status: Option<Status>,
    pub(crate) comparator: Option<(CmpOperator, bool)>,
    pub(crate) children: Vec<StatusContext>,
}

impl StatusContext {
    fn new(eval_type: EvaluationType, context: &str) -> Self {
        StatusContext {
            eval_type,
            context: context.to_string(),
            status: None,
            msg: None,
            from: None,
            to: None,
            comparator: None,
            children: vec![],
        }
    }
}

pub(crate) struct StackTracker<'r> {
    root_context: &'r dyn EvaluationContext,
    stack: std::cell::RefCell<Vec<StatusContext>>,
}

impl<'r> std::fmt::Debug for StackTracker<'r> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        self.stack.borrow().fmt(f)
    }
}

impl<'r> EvaluationContext for StackTracker<'r> {
    fn resolve_variable(&self, variable: &str) -> Result<Vec<&PathAwareValue>> {
        self.root_context.resolve_variable(variable)
    }

    fn rule_status(&self, rule_name: &str) -> Result<Status> {
        self.root_context.rule_status(rule_name)
    }

    fn end_evaluation(
        &self,
        eval_type: EvaluationType,
        context: &str,
        msg: String,
        from: Option<PathAwareValue>,
        to: Option<PathAwareValue>,
        status: Option<Status>,
        cmp: Option<(CmpOperator, bool)>,
    ) {
        if self.stack.borrow().len() == 1 {
            match self.stack.borrow_mut().get_mut(0) {
                Some(top) => {
                    top.status = status;
                    top.from = from;
                    top.to = to;
                    top.msg = Some(msg);
                    top.comparator = cmp;
                }
                None => unreachable!(),
            }
            return;
        }

        let stack = self.stack.borrow_mut().pop();
        if let Some(mut stack) = stack {
            stack.status = status;
            stack.from = from.clone();
            stack.to = to.clone();
            stack.msg = Some(msg.clone());
            stack.comparator = cmp;

            match self.stack.borrow_mut().last_mut() {
                Some(cxt) => cxt.children.push(stack),
                None => unreachable!(),
            }
        }
        self.root_context
            .end_evaluation(eval_type, context, msg, from, to, status, cmp);
    }

    fn start_evaluation(&self, eval_type: EvaluationType, context: &str) {
        let _indent = self.stack.borrow().len();
        self.stack
            .borrow_mut()
            .push(StatusContext::new(eval_type, context));
        self.root_context.start_evaluation(eval_type, context);
    }
}