fn evaluate()

in guard/src/rules/evaluate.rs [295:599]


    fn evaluate<'s>(&self,
                context: &'s PathAwareValue,
                var_resolver: &'s dyn EvaluationContext) -> Result<Status> {
        //var_resolver.start_evaluation(EvaluationType::Clause, &guard_loc);
        let clause = self;

        let all = self.access_clause.query.match_all;


        let (lhs, retrieve_error) =
            match resolve_query(clause.access_clause.query.match_all,
                                &clause.access_clause.query.query,
                                context,
                                var_resolver)
            {
                Ok(v) => (Some(v), None),
                Err(Error(ErrorKind::RetrievalError(e))) |
                Err(Error(ErrorKind::IncompatibleRetrievalError(e))) => (None, Some(e)),
                Err(e) => return Err(e),
            };

        let result = match clause.access_clause.comparator {
            (CmpOperator::Empty, not) =>
                //
                // Retrieval Error is considered the same as an empty or !exists
                // When using "SOME" keyword in the clause, then IncompatibleError is trapped to be none
                // This is okay as long as the checks are for empty, exists
                //
                match &lhs {
                    None => Some(negation_status(true, not, clause.negation)),
                    Some(l) => {
                        Some(
                            if !l.is_empty() {
                                if l[0].is_list() || l[0].is_map() {
                                    'all_empty: loop {
                                        for element in l {
                                            let status = match *element {
                                                PathAwareValue::List((_, v)) =>
                                                    negation_status(v.is_empty(), not, clause.negation),
                                                PathAwareValue::Map((_, m)) =>
                                                    negation_status(m.is_empty(), not, clause.negation),
                                                _ => continue
                                            };

                                            if status == Status::FAIL {
                                                break 'all_empty Status::FAIL;
                                            }
                                        }
                                        break Status::PASS
                                    }
                                }
                                else {
                                    negation_status(false, not, clause.negation)
                                }
                            }
                            else {
                                negation_status(true, not, clause.negation)
                            })
                    }
                },

            (CmpOperator::Exists, not) =>
                match &lhs {
                    None => Some(negation_status(false, not, clause.negation)),
                    Some(_) => Some(negation_status(true, not, clause.negation)),
                },

            (CmpOperator::Eq, not) =>
                match &clause.access_clause.compare_with {
                    Some(LetValue::Value(Value::Null)) =>
                        match &lhs {
                            None => Some(negation_status(true, not, clause.negation)),
                            Some(_) => Some(negation_status(false, not, clause.negation)),
                        }
                    _ => None
                },

            (CmpOperator::IsString, not) =>
                match &lhs {
                    None => Some(negation_status(false, not, clause.negation)),
                    Some(l) => Some(
                        negation_status( l.iter().find(|p|
                            if let PathAwareValue::String(_) = **p {
                                false
                            } else {
                                true
                            }
                        ).map_or(true, |_i| false), not, clause.negation))
                },

            (CmpOperator::IsList, not) =>
                match &lhs {
                    None => Some(negation_status(false, not, clause.negation)),
                    Some(l) => Some(
                        negation_status( l.iter().find(|p|
                            if let PathAwareValue::List(_) = **p {
                                false
                            } else {
                                true
                            }
                        ).map_or(true, |_i| false), not, clause.negation))
                },

            (CmpOperator::IsMap, not) =>
                match &lhs {
                    None => Some(negation_status(false, not, clause.negation)),
                    Some(l) => Some(
                        negation_status( l.iter().find(|p|
                            if let PathAwareValue::Map(_) = **p {
                                false
                            } else {
                                true
                            }
                        ).map_or(true, |_i| false), not, clause.negation))
                },

            _ => None
        };

        if let Some(r) = result {
            let guard_loc = format!("{}", self);
            let mut auto_reporter = AutoReport::new(EvaluationType::Clause, var_resolver, &guard_loc);
            let message = match &clause.access_clause.custom_message {
                Some(msg) => msg,
                None => "(DEFAULT: NO_MESSAGE)"
            };
            auto_reporter.cmp(self.access_clause.comparator).status(r).from(
                match &lhs {
                    None => Some(context.clone()),
                    Some(l) => if !l.is_empty() {
                        Some(l[0].clone())
                    } else { Some(context.clone()) }
                }
            );
            if r == Status::FAIL {
                auto_reporter.message(message.to_string());
            }
            return Ok(r)
        }

        let lhs = match lhs {
            None => {
                let guard_loc = format!("{}", self);
                let mut auto_reporter = AutoReport::new(EvaluationType::Clause, var_resolver, &guard_loc);
                if all {
                    return Ok(auto_reporter.status(Status::FAIL)
                        .message(retrieve_error.map_or("".to_string(), |e| e)).get_status())
                } else {
                    return Ok(auto_reporter.status(Status::FAIL)
                        .message(retrieve_error.map_or("".to_string(), |e| e)).get_status())
                }
            }
            Some(l) => l,
        };

        let rhs_local = match &clause.access_clause.compare_with {
            None => return Err(Error::new(ErrorKind::IncompatibleRetrievalError(
                format!("Expecting a RHS for comparison and did not find one, clause@{}",
                        clause.access_clause.location)
            ))),

            Some(expr) => {
                match expr {
                    LetValue::Value(v) => {
                        let path = format!("{}/{}/{}/Clause/",
                            clause.access_clause.location.file_name,
                            clause.access_clause.location.line,
                            clause.access_clause.location.column);
                        let path = super::path_value::Path(path);
                        Some(vec![PathAwareValue::try_from((v, path))?])
                    },

                    _ => None,
                }
            }
        };

        let (rhs_resolved, rhs_query) = if let Some(expr) = &clause.access_clause.compare_with {
            match expr {
                LetValue::AccessClause(query) =>
                    (Some(resolve_query(query.match_all, &query.query, context, var_resolver)?), Some(query.query.as_slice())),
                _ => (None, None)
            }
        } else {
            (None, None)
        };

        let rhs = match &rhs_local {
            Some(local) => local.iter().collect::<Vec<&PathAwareValue>>(),
            None => match rhs_resolved {
                Some(resolved) => resolved,
                None => unreachable!()
            }
        };

        let (result, outcomes) =
            match &clause.access_clause.comparator.0 {
            //
            // ==, !=
            //
            CmpOperator::Eq =>
                compare(&lhs,
                        &clause.access_clause.query.query,
                        &rhs,
                        rhs_query,
                        invert_closure(super::path_value::compare_eq, clause.access_clause.comparator.1, clause.negation),
                        false,
                        !all)?,

            //
            // >
            //
            CmpOperator::Gt =>
                compare(&lhs,
                        &clause.access_clause.query.query,
                        &rhs,
                        rhs_query,
                        invert_closure(super::path_value::compare_gt, clause.access_clause.comparator.1, clause.negation),
                        false,
                        !all)?,

            //
            // >=
            //
            CmpOperator::Ge =>
                compare(&lhs,
                        &clause.access_clause.query.query,
                        &rhs,
                        rhs_query,
                        invert_closure(super::path_value::compare_ge, clause.access_clause.comparator.1, clause.negation),
                        false,
                        !all)?,

            //
            // <
            //
            CmpOperator::Lt =>
                compare(&lhs,
                        &clause.access_clause.query.query,
                        &rhs,
                        rhs_query,
                        invert_closure(super::path_value::compare_lt, clause.access_clause.comparator.1, clause.negation),
                        false,
                        !all)?,

            //
            // <=
            //
            CmpOperator::Le =>
                compare(&lhs,
                        &clause.access_clause.query.query,
                        &rhs,
                        rhs_query,
                        invert_closure(super::path_value::compare_le, clause.access_clause.comparator.1, clause.negation),
                        false,
                        !all)?,

            //
            // IN, !IN
            //
            CmpOperator::In => {
                let mut result = if clause.access_clause.comparator.1 {
                    //
                    // ! IN operator
                    //
                    compare(&lhs,
                            &clause.access_clause.query.query,
                            &rhs,
                            rhs_query,
                            |lhs, rhs| {
                                Ok(!super::path_value::compare_eq(lhs, rhs)?)
                            },
                            false,
                            !all)?
                } else {
                    compare(&lhs,
                            &clause.access_clause.query.query,
                            &rhs,
                            rhs_query,
                            super::path_value::compare_eq,
                            true,
                            !all)?
                };
                result.0 = invert_status(result.0, clause.negation);
                result
            },

            _ => unreachable!()

        };

        for (outcome, from, to) in outcomes {
            let guard_loc = format!("{}", self);
            let mut auto_reporter = AutoReport::new(EvaluationType::Clause, var_resolver, &guard_loc);
            auto_reporter.status(if outcome { Status::PASS } else { Status::FAIL });
            auto_reporter.cmp(clause.access_clause.comparator);
            if !outcome {
                auto_reporter.from(from).to(to).message(match &clause.access_clause.custom_message {
                    Some(msg) => msg.clone(),
                    None => "DEFAULT MESSAGE(FAIL)".to_string()
                });
            }
        }
        Ok(result)
    }