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)
}