1use crate::ast::*;
20use crate::entities::{Dereference, Entities};
21use crate::extensions::Extensions;
22use crate::parser::Loc;
23#[cfg(feature = "partial-eval")]
24use std::collections::BTreeMap;
25use std::sync::Arc;
26
27mod err;
28#[cfg(feature = "tolerant-ast")]
29use crate::evaluator::EvaluationError::ASTErrorExpr;
30pub use err::evaluation_errors;
31pub use err::EvaluationError;
32pub(crate) use err::*;
33use evaluation_errors::*;
34use itertools::{Either, Itertools};
35use nonempty::nonempty;
36use smol_str::SmolStr;
37
38const REQUIRED_STACK_SPACE: usize = 1024 * 100;
39
40#[cfg(feature = "partial-eval")]
41type UnknownsMapper<'e> = Box<dyn Fn(&str) -> Option<Value> + 'e>;
42
43#[allow(clippy::expect_used)]
45mod names {
46 use super::Name;
47 use std::sync::LazyLock;
48
49 pub static ANY_ENTITY_TYPE: LazyLock<Name> = LazyLock::new(|| {
50 Name::parse_unqualified_name("any_entity_type").expect("valid identifier")
51 });
52}
53
54pub fn unary_app(op: UnaryOp, arg: Value, loc: Option<&Loc>) -> Result<Value> {
56 match op {
57 UnaryOp::Not => match arg.get_as_bool()? {
58 true => Ok(false.into()),
59 false => Ok(true.into()),
60 },
61 UnaryOp::Neg => {
62 let i = arg.get_as_long()?;
63 match i.checked_neg() {
64 Some(v) => Ok(v.into()),
65 None => Err(IntegerOverflowError::UnaryOp(UnaryOpOverflowError {
66 op,
67 arg,
68 source_loc: loc.cloned(),
69 })
70 .into()),
71 }
72 }
73 UnaryOp::IsEmpty => {
74 let s = arg.get_as_set()?;
75 Ok(s.is_empty().into())
76 }
77 }
78}
79
80pub fn binary_relation(
82 op: BinaryOp,
83 arg1: &Value,
84 arg2: &Value,
85 extensions: &Extensions<'_>,
86) -> Result<Value> {
87 match op {
88 BinaryOp::Eq => Ok((arg1 == arg2).into()),
89 BinaryOp::Less | BinaryOp::LessEq => {
91 let long_op = if matches!(op, BinaryOp::Less) {
92 |x, y| x < y
93 } else {
94 |x, y| x <= y
95 };
96 let ext_op = if matches!(op, BinaryOp::Less) {
97 |x, y| x < y
98 } else {
99 |x, y| x <= y
100 };
101 match (arg1.value_kind(), arg2.value_kind()) {
102 (ValueKind::Lit(Literal::Long(x)), ValueKind::Lit(Literal::Long(y))) => {
103 Ok(long_op(x, y).into())
104 }
105 (ValueKind::ExtensionValue(x), ValueKind::ExtensionValue(y))
106 if x.supports_operator_overloading()
107 && y.supports_operator_overloading()
108 && x.typename() == y.typename() =>
109 {
110 Ok(ext_op(x, y).into())
111 }
112 (ValueKind::Lit(Literal::Long(_)), _) => {
114 Err(EvaluationError::type_error_single(Type::Long, arg2))
115 }
116 (_, ValueKind::Lit(Literal::Long(_))) => {
117 Err(EvaluationError::type_error_single(Type::Long, arg1))
118 }
119 (ValueKind::ExtensionValue(x), _) if x.supports_operator_overloading() => {
120 Err(EvaluationError::type_error_single(
121 Type::Extension { name: x.typename() },
122 arg2,
123 ))
124 }
125 (_, ValueKind::ExtensionValue(y)) if y.supports_operator_overloading() => {
126 Err(EvaluationError::type_error_single(
127 Type::Extension { name: y.typename() },
128 arg1,
129 ))
130 }
131 _ => {
132 let expected_types = valid_comparison_op_types(extensions);
133 Err(EvaluationError::type_error_with_advice(
134 expected_types.clone(),
135 arg1,
136 format!(
137 "Only types {} support comparison",
138 expected_types.into_iter().sorted().join(", ")
139 ),
140 ))
141 }
142 }
143 }
144 #[allow(clippy::unreachable)]
146 _ => {
147 unreachable!("Should have already checked that op was one of these")
148 }
149 }
150}
151
152pub fn binary_arith(op: BinaryOp, arg1: Value, arg2: Value, loc: Option<&Loc>) -> Result<Value> {
154 let i1 = arg1.get_as_long()?;
155 let i2 = arg2.get_as_long()?;
156 match op {
157 BinaryOp::Add => match i1.checked_add(i2) {
158 Some(sum) => Ok(sum.into()),
159 None => Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
160 op,
161 arg1,
162 arg2,
163 source_loc: loc.cloned(),
164 })
165 .into()),
166 },
167 BinaryOp::Sub => match i1.checked_sub(i2) {
168 Some(diff) => Ok(diff.into()),
169 None => Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
170 op,
171 arg1,
172 arg2,
173 source_loc: loc.cloned(),
174 })
175 .into()),
176 },
177 BinaryOp::Mul => match i1.checked_mul(i2) {
178 Some(prod) => Ok(prod.into()),
179 None => Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
180 op,
181 arg1,
182 arg2,
183 source_loc: loc.cloned(),
184 })
185 .into()),
186 },
187 #[allow(clippy::unreachable)]
189 _ => {
190 unreachable!("Should have already checked that op was one of these")
191 }
192 }
193}
194
195pub struct Evaluator<'e> {
201 principal: EntityUIDEntry,
203 action: EntityUIDEntry,
205 resource: EntityUIDEntry,
207 context: PartialValue,
209 entities: &'e Entities,
215 extensions: &'e Extensions<'e>,
217 #[cfg(feature = "partial-eval")]
219 unknowns_mapper: UnknownsMapper<'e>,
220}
221
222#[derive(Debug)]
224pub struct RestrictedEvaluator<'e> {
225 extensions: &'e Extensions<'e>,
227}
228
229impl<'e> RestrictedEvaluator<'e> {
230 pub fn new(extensions: &'e Extensions<'e>) -> Self {
232 Self { extensions }
233 }
234
235 pub fn interpret(&self, e: BorrowedRestrictedExpr<'_>) -> Result<Value> {
239 match self.partial_interpret(e)? {
240 PartialValue::Value(v) => Ok(v),
241 PartialValue::Residual(r) => Err(EvaluationError::non_value(r)),
242 }
243 }
244
245 pub fn partial_interpret(&self, expr: BorrowedRestrictedExpr<'_>) -> Result<PartialValue> {
251 stack_size_check()?;
252
253 let res = self.partial_interpret_internal(expr);
254
255 res.map(|pval| pval.with_maybe_source_loc(expr.source_loc().cloned()))
263 .map_err(|err| {
264 if err.source_loc().is_none() {
265 err.with_maybe_source_loc(expr.source_loc().cloned())
266 } else {
267 err
268 }
269 })
270 }
271
272 fn partial_interpret_internal(&self, expr: BorrowedRestrictedExpr<'_>) -> Result<PartialValue> {
284 match expr.as_ref().expr_kind() {
285 ExprKind::Lit(lit) => Ok(lit.clone().into()),
286 ExprKind::Set(items) => {
287 let vals = items
288 .iter()
289 .map(|item| self.partial_interpret(BorrowedRestrictedExpr::new_unchecked(item))) .collect::<Result<Vec<_>>>()?;
291 match split(vals) {
292 Either::Left(values) => Ok(Value::set(values, expr.source_loc().cloned()).into()),
293 Either::Right(residuals) => Ok(Expr::set(residuals).into()),
294 }
295 }
296 ExprKind::Unknown(u) => Ok(PartialValue::unknown(u.clone())),
297 ExprKind::Record(map) => {
298 let map = map
299 .iter()
300 .map(|(k, v)| Ok((k.clone(), self.partial_interpret(BorrowedRestrictedExpr::new_unchecked(v))?))) .collect::<Result<Vec<_>>>()?;
302 let (names, attrs) : (Vec<_>, Vec<_>) = map.into_iter().unzip();
303 match split(attrs) {
304 Either::Left(values) => Ok(Value::record(names.into_iter().zip(values), expr.source_loc().cloned()).into()),
305 Either::Right(residuals) => {
306 #[allow(clippy::expect_used)]
308 Ok(
309 Expr::record(names.into_iter().zip(residuals))
310 .expect("can't have a duplicate key here because `names` is the set of keys of the input `BTreeMap`")
311 .into()
312 )
313 }
314 }
315 }
316 ExprKind::ExtensionFunctionApp { fn_name, args } => {
317 let args = args
318 .iter()
319 .map(|arg| self.partial_interpret(BorrowedRestrictedExpr::new_unchecked(arg))) .collect::<Result<Vec<_>>>()?;
321 match split(args) {
322 Either::Left(values) => {
323 let values : Vec<_> = values.collect();
324 let efunc = self.extensions.func(fn_name)?;
325 efunc.call(&values)
326 },
327 Either::Right(residuals) => Ok(Expr::call_extension_fn(fn_name.clone(), residuals.collect()).into()),
328 }
329 },
330 #[allow(clippy::unreachable)]
332 expr => unreachable!("internal invariant violation: BorrowedRestrictedExpr somehow contained this expr case: {expr:?}"),
333 }
334 }
335}
336
337pub(crate) fn valid_comparison_op_types(extensions: &Extensions<'_>) -> nonempty::NonEmpty<Type> {
338 let mut expected_types = nonempty::NonEmpty::singleton(Type::Long);
339 expected_types.extend(
340 extensions
341 .types_with_operator_overloading()
342 .map(|n| Type::Extension { name: n.clone() }),
343 );
344 expected_types
345}
346
347impl<'e> Evaluator<'e> {
348 pub fn new(q: Request, entities: &'e Entities, extensions: &'e Extensions<'e>) -> Self {
352 Self {
353 principal: q.principal,
354 action: q.action,
355 resource: q.resource,
356 context: {
357 match q.context {
358 None => PartialValue::unknown(Unknown::new_untyped("context")),
359 Some(ctx) => ctx.into(),
360 }
361 },
362 entities,
363 extensions,
364 #[cfg(feature = "partial-eval")]
365 unknowns_mapper: Box::new(|_: &str| -> Option<Value> { None }),
366 }
367 }
368
369 #[cfg(feature = "partial-eval")]
371 pub(crate) fn with_unknowns_mapper(self, unknowns_mapper: UnknownsMapper<'e>) -> Self {
372 Self {
373 principal: self.principal,
374 action: self.action,
375 resource: self.resource,
376 context: self.context,
377 entities: self.entities,
378 extensions: self.extensions,
379 unknowns_mapper,
380 }
381 }
382
383 pub fn evaluate(&self, p: &Policy) -> Result<bool> {
390 self.interpret(&p.condition(), p.env())?.get_as_bool()
391 }
392
393 pub fn partial_evaluate(&self, p: &Policy) -> Result<Either<bool, Expr>> {
403 match self.partial_interpret(&p.condition(), p.env())? {
404 PartialValue::Value(v) => v.get_as_bool().map(Either::Left),
405 PartialValue::Residual(e) => Ok(Either::Right(e)),
406 }
407 }
408
409 pub fn interpret(&self, e: &Expr, slots: &SlotEnv) -> Result<Value> {
415 match self.partial_interpret(e, slots)? {
416 PartialValue::Value(v) => Ok(v),
417 PartialValue::Residual(r) => Err(EvaluationError::non_value(r)),
418 }
419 }
420
421 pub fn partial_interpret(&self, expr: &Expr, slots: &SlotEnv) -> Result<PartialValue> {
427 stack_size_check()?;
428
429 let res = self.partial_interpret_internal(expr, slots);
430
431 res.map(|pval| pval.with_maybe_source_loc(expr.source_loc().cloned()))
439 .map_err(|err| {
440 if err.source_loc().is_none() {
441 err.with_maybe_source_loc(expr.source_loc().cloned())
442 } else {
443 err
444 }
445 })
446 }
447
448 #[allow(clippy::cognitive_complexity)]
458 fn partial_interpret_internal(&self, expr: &Expr, slots: &SlotEnv) -> Result<PartialValue> {
459 let loc = expr.source_loc(); match expr.expr_kind() {
461 ExprKind::Lit(lit) => Ok(lit.clone().into()),
462 ExprKind::Slot(id) => slots
463 .get(id)
464 .ok_or_else(|| err::EvaluationError::unlinked_slot(*id, loc.cloned()))
465 .map(|euid| PartialValue::from(euid.clone())),
466 ExprKind::Var(v) => match v {
467 Var::Principal => Ok(self.principal.evaluate(*v)),
468 Var::Action => Ok(self.action.evaluate(*v)),
469 Var::Resource => Ok(self.resource.evaluate(*v)),
470 Var::Context => Ok(self.context.clone()),
471 },
472 ExprKind::Unknown(u) => self.unknown_to_partialvalue(u),
473 ExprKind::If {
474 test_expr,
475 then_expr,
476 else_expr,
477 } => self.eval_if(test_expr, then_expr, else_expr, slots),
478 ExprKind::And { left, right } => {
479 match self.partial_interpret(left, slots)? {
480 PartialValue::Residual(e) => {
482 Ok(PartialValue::Residual(Expr::and(e, right.as_ref().clone())))
483 }
484 PartialValue::Value(v) => {
486 if v.get_as_bool()? {
487 match self.partial_interpret(right, slots)? {
488 PartialValue::Residual(right) => {
492 Ok(PartialValue::Residual(Expr::and(Expr::val(true), right)))
493 }
494 PartialValue::Value(v) => Ok(v.get_as_bool()?.into()),
496 }
497 } else {
498 Ok(false.into())
500 }
501 }
502 }
503 }
504 ExprKind::Or { left, right } => {
505 match self.partial_interpret(left, slots)? {
506 PartialValue::Residual(r) => {
508 Ok(PartialValue::Residual(Expr::or(r, right.as_ref().clone())))
509 }
510 PartialValue::Value(lhs) => {
512 if lhs.get_as_bool()? {
513 Ok(true.into())
515 } else {
516 match self.partial_interpret(right, slots)? {
517 PartialValue::Residual(rhs) =>
518 {
522 Ok(PartialValue::Residual(Expr::or(Expr::val(false), rhs)))
523 }
524 PartialValue::Value(v) => Ok(v.get_as_bool()?.into()),
525 }
526 }
527 }
528 }
529 }
530 ExprKind::UnaryApp { op, arg } => match self.partial_interpret(arg, slots)? {
531 PartialValue::Value(arg) => unary_app(*op, arg, loc).map(Into::into),
532 PartialValue::Residual(r) => Ok(PartialValue::Residual(Expr::unary_app(*op, r))),
535 },
536 ExprKind::BinaryApp { op, arg1, arg2 } => {
537 let (arg1, arg2) = match (
542 self.partial_interpret(arg1, slots)?,
543 self.partial_interpret(arg2, slots)?,
544 ) {
545 (PartialValue::Value(v1), PartialValue::Value(v2)) => (v1, v2),
546 (PartialValue::Value(v1), PartialValue::Residual(e2)) => {
547 if let Some(val) = self.short_circuit_value_and_residual(&v1, &e2, *op) {
548 return Ok(val);
549 }
550 return Ok(PartialValue::Residual(Expr::binary_app(*op, v1.into(), e2)));
551 }
552 (PartialValue::Residual(e1), PartialValue::Value(v2)) => {
553 if let Some(val) = self.short_circuit_residual_and_value(&e1, &v2, *op) {
554 return Ok(val);
555 }
556 return Ok(PartialValue::Residual(Expr::binary_app(*op, e1, v2.into())));
557 }
558 (PartialValue::Residual(e1), PartialValue::Residual(e2)) => {
559 if let Some(val) = self.short_circuit_two_typed_residuals(&e1, &e2, *op) {
560 return Ok(val);
561 }
562 return Ok(PartialValue::Residual(Expr::binary_app(*op, e1, e2)));
563 }
564 };
565 match op {
566 BinaryOp::Eq | BinaryOp::Less | BinaryOp::LessEq => {
567 binary_relation(*op, &arg1, &arg2, self.extensions).map(Into::into)
568 }
569 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul => {
570 binary_arith(*op, arg1, arg2, loc).map(Into::into)
571 }
572 BinaryOp::In => {
574 let uid1 = arg1.get_as_entity().map_err(|mut e|
575 {
576 if let EvaluationError::TypeError(TypeError { advice, .. }) = &mut e {
580 match arg2.type_of() {
581 Type::Set => *advice = Some("`in` is for checking the entity hierarchy; use `.contains()` to test set membership".into()),
582 Type::Record => *advice = Some("`in` is for checking the entity hierarchy; use `has` to test if a record has a key".into()),
583 _ => {}
584 }
585 };
586 e
587 })?;
588 match self.entities.entity(uid1) {
589 Dereference::Residual(r) => Ok(PartialValue::Residual(
590 Expr::binary_app(BinaryOp::In, r, arg2.into()),
591 )),
592 Dereference::NoSuchEntity => self.eval_in(uid1, None, arg2),
593 Dereference::Data(entity1) => self.eval_in(uid1, Some(entity1), arg2),
594 }
595 }
596 BinaryOp::Contains => {
598 if let Ok(s) = arg1.get_as_set() {
599 Ok(s.contains(&arg2).into())
600 } else {
601 Err(EvaluationError::type_error_single(Type::Set, &arg1))
602 }
603 }
604 BinaryOp::ContainsAll => {
606 let arg1_set = arg1.get_as_set()?;
607 let arg2_set = arg2.get_as_set()?;
608
609 Ok((arg2_set.is_subset(arg1_set)).into())
610 }
611 BinaryOp::ContainsAny => {
613 let arg1_set = arg1.get_as_set()?;
614 let arg2_set = arg2.get_as_set()?;
615 Ok((!arg1_set.is_disjoint(arg2_set)).into())
616 }
617 BinaryOp::GetTag | BinaryOp::HasTag => {
619 let uid = arg1.get_as_entity()?;
620 let tag = arg2.get_as_string()?;
621 match op {
622 BinaryOp::GetTag => {
623 match self.entities.entity(uid) {
624 Dereference::NoSuchEntity => {
625 Err(EvaluationError::entity_does_not_exist(
627 Arc::new(uid.clone()),
628 arg1.source_loc().cloned(),
629 ))
630 }
631 Dereference::Residual(r) => Ok(PartialValue::Residual(
632 Expr::get_tag(r, Expr::val(tag.clone())),
633 )),
634 Dereference::Data(entity) => entity
635 .get_tag(tag)
636 .ok_or_else(|| {
637 EvaluationError::entity_tag_does_not_exist(
638 Arc::new(uid.clone()),
639 tag.clone(),
640 entity.tag_keys(),
641 entity.get(tag).is_some(),
642 entity.tags_len(),
643 loc.cloned(), )
645 })
646 .cloned(),
647 }
648 }
649 BinaryOp::HasTag => match self.entities.entity(uid) {
650 Dereference::NoSuchEntity => Ok(false.into()),
651 Dereference::Residual(r) => Ok(PartialValue::Residual(
652 Expr::has_tag(r, Expr::val(tag.clone())),
653 )),
654 Dereference::Data(entity) => {
655 Ok(entity.get_tag(tag).is_some().into())
656 }
657 },
658 #[allow(clippy::unreachable)]
660 _ => {
661 unreachable!("Should have already checked that op was one of these")
662 }
663 }
664 }
665 }
666 }
667 ExprKind::ExtensionFunctionApp { fn_name, args } => {
668 let args = args
669 .iter()
670 .map(|arg| self.partial_interpret(arg, slots))
671 .collect::<Result<Vec<_>>>()?;
672 match split(args) {
673 Either::Left(vals) => {
674 let vals: Vec<_> = vals.collect();
675 let efunc = self.extensions.func(fn_name)?;
676 efunc.call(&vals)
677 }
678 Either::Right(residuals) => Ok(PartialValue::Residual(
679 Expr::call_extension_fn(fn_name.clone(), residuals.collect()),
680 )),
681 }
682 }
683 ExprKind::GetAttr { expr, attr } => self.get_attr(expr.as_ref(), attr, slots, loc),
684 ExprKind::HasAttr { expr, attr } => match self.partial_interpret(expr, slots)? {
685 PartialValue::Value(Value {
686 value: ValueKind::Record(record),
687 ..
688 }) => Ok(record.get(attr).is_some().into()),
689 PartialValue::Value(Value {
690 value: ValueKind::Lit(Literal::EntityUID(uid)),
691 ..
692 }) => match self.entities.entity(&uid) {
693 Dereference::NoSuchEntity => Ok(false.into()),
694 Dereference::Residual(r) => {
695 Ok(PartialValue::Residual(Expr::has_attr(r, attr.clone())))
696 }
697 Dereference::Data(e) => Ok(e.get(attr).is_some().into()),
698 },
699 PartialValue::Value(val) => Err(err::EvaluationError::type_error(
700 nonempty![
701 Type::Record,
702 Type::entity_type(names::ANY_ENTITY_TYPE.clone())
703 ],
704 &val,
705 )),
706 PartialValue::Residual(r) => Ok(Expr::has_attr(r, attr.clone()).into()),
707 },
708 ExprKind::Like { expr, pattern } => {
709 let v = self.partial_interpret(expr, slots)?;
710 match v {
711 PartialValue::Value(v) => {
712 Ok((pattern.wildcard_match(v.get_as_string()?)).into())
713 }
714 PartialValue::Residual(r) => Ok(Expr::like(r, pattern.clone()).into()),
715 }
716 }
717 ExprKind::Is { expr, entity_type } => {
718 let v = self.partial_interpret(expr, slots)?;
719 match v {
720 PartialValue::Value(v) => {
721 Ok((v.get_as_entity()?.entity_type() == entity_type).into())
722 }
723 PartialValue::Residual(r) => {
724 if let ExprKind::Unknown(Unknown {
725 type_annotation:
726 Some(Type::Entity {
727 ty: type_of_unknown,
728 }),
729 ..
730 }) = r.expr_kind()
731 {
732 return Ok((type_of_unknown == entity_type).into());
733 }
734 Ok(Expr::is_entity_type(r, entity_type.clone()).into())
735 }
736 }
737 }
738 ExprKind::Set(items) => {
739 let vals = items
740 .iter()
741 .map(|item| self.partial_interpret(item, slots))
742 .collect::<Result<Vec<_>>>()?;
743 match split(vals) {
744 Either::Left(vals) => Ok(Value::set(vals, loc.cloned()).into()),
745 Either::Right(r) => Ok(Expr::set(r).into()),
746 }
747 }
748 ExprKind::Record(map) => {
749 let map = map
750 .iter()
751 .map(|(k, v)| Ok((k.clone(), self.partial_interpret(v, slots)?)))
752 .collect::<Result<Vec<_>>>()?;
753 let (names, evalled): (Vec<SmolStr>, Vec<PartialValue>) = map.into_iter().unzip();
754 match split(evalled) {
755 Either::Left(vals) => {
756 Ok(Value::record(names.into_iter().zip(vals), loc.cloned()).into())
757 }
758 Either::Right(rs) => {
759 #[allow(clippy::expect_used)]
761 Ok(
762 Expr::record(names.into_iter().zip(rs))
763 .expect("can't have a duplicate key here because `names` is the set of keys of the input `BTreeMap`")
764 .into()
765 )
766 }
767 }
768 }
769 #[cfg(feature = "tolerant-ast")]
770 ExprKind::Error { .. } => Err(ASTErrorExpr(ASTErrorExprError {
771 source_loc: loc.cloned(),
772 })),
773 }
774 }
775
776 #[cfg(not(feature = "partial-eval"))]
778 #[inline(always)]
779 fn unknown_to_partialvalue(&self, u: &Unknown) -> Result<PartialValue> {
780 Ok(PartialValue::Residual(Expr::unknown(u.clone())))
781 }
782
783 #[cfg(feature = "partial-eval")]
785 fn unknown_to_partialvalue(&self, u: &Unknown) -> Result<PartialValue> {
786 match (self.unknowns_mapper.as_ref()(&u.name), &u.type_annotation) {
787 (None, _) => Ok(PartialValue::Residual(Expr::unknown(u.clone()))),
789 (Some(v), None) => Ok(PartialValue::Value(v)),
791 (Some(v), Some(t)) => {
792 if v.type_of() == *t {
793 Ok(PartialValue::Value(v))
794 } else {
795 Err(EvaluationError::type_error_single(t.clone(), &v))
796 }
797 }
798 }
799 }
800
801 fn eval_in(
802 &self,
803 uid1: &EntityUID,
804 entity1: Option<&Entity>,
805 arg2: Value,
806 ) -> Result<PartialValue> {
807 let rhs = match arg2.value {
810 ValueKind::Lit(Literal::EntityUID(uid)) => vec![Arc::unwrap_or_clone(uid)],
811 ValueKind::Set(Set { authoritative, .. }) => authoritative
814 .iter()
815 .map(|val| Ok(val.get_as_entity()?.clone()))
816 .collect::<Result<Vec<EntityUID>>>()?,
817 _ => {
818 return Err(EvaluationError::type_error(
819 nonempty![Type::Set, Type::entity_type(names::ANY_ENTITY_TYPE.clone())],
820 &arg2,
821 ))
822 }
823 };
824 for uid2 in rhs {
825 if uid1 == &uid2
826 || entity1
827 .map(|e1| e1.is_descendant_of(&uid2))
828 .unwrap_or(false)
829 {
830 return Ok(true.into());
831 }
832 }
833 Ok(false.into())
836 }
837
838 fn eval_if(
841 &self,
842 guard: &Expr,
843 consequent: &Arc<Expr>,
844 alternative: &Arc<Expr>,
845 slots: &SlotEnv,
846 ) -> Result<PartialValue> {
847 match self.partial_interpret(guard, slots)? {
848 PartialValue::Value(v) => {
849 if v.get_as_bool()? {
850 self.partial_interpret(consequent, slots)
851 } else {
852 self.partial_interpret(alternative, slots)
853 }
854 }
855 PartialValue::Residual(guard) => {
856 Ok(Expr::ite_arc(Arc::new(guard), consequent.clone(), alternative.clone()).into())
857 }
858 }
859 }
860
861 fn get_attr(
865 &self,
866 expr: &Expr,
867 attr: &SmolStr,
868 slots: &SlotEnv,
869 source_loc: Option<&Loc>,
870 ) -> Result<PartialValue> {
871 match self.partial_interpret(expr, slots)? {
872 PartialValue::Residual(res) => {
874 match res.expr_kind() {
875 ExprKind::Record(map) => {
876 if res.is_projectable() {
881 map.as_ref()
882 .iter()
883 .filter_map(|(k, v)| if k == attr { Some(v) } else { None })
884 .next()
885 .ok_or_else(|| {
886 EvaluationError::record_attr_does_not_exist(
887 attr.clone(),
888 map.keys(),
889 map.len(),
890 source_loc.cloned(),
891 )
892 })
893 .and_then(|e| self.partial_interpret(e, slots))
894 } else if map.keys().any(|k| k == attr) {
895 Ok(PartialValue::Residual(Expr::get_attr(
896 Expr::record_arc(Arc::clone(map)),
897 attr.clone(),
898 )))
899 } else {
900 Err(EvaluationError::record_attr_does_not_exist(
901 attr.clone(),
902 map.keys(),
903 map.len(),
904 source_loc.cloned(),
905 ))
906 }
907 }
908 _ => Ok(PartialValue::Residual(Expr::get_attr(res, attr.clone()))),
910 }
911 }
912 PartialValue::Value(Value {
913 value: ValueKind::Record(record),
914 ..
915 }) => record
916 .as_ref()
917 .get(attr)
918 .ok_or_else(|| {
919 EvaluationError::record_attr_does_not_exist(
920 attr.clone(),
921 record.keys(),
922 record.len(),
923 source_loc.cloned(),
924 )
925 })
926 .map(|v| PartialValue::Value(v.clone())),
927 PartialValue::Value(Value {
928 value: ValueKind::Lit(Literal::EntityUID(uid)),
929 loc,
930 }) => match self.entities.entity(uid.as_ref()) {
931 Dereference::NoSuchEntity => {
932 Err(EvaluationError::entity_does_not_exist(uid.clone(), loc))
934 }
935 Dereference::Residual(r) => {
936 Ok(PartialValue::Residual(Expr::get_attr(r, attr.clone())))
937 }
938 Dereference::Data(entity) => entity
939 .get(attr)
940 .map(|pv| match pv {
941 PartialValue::Value(_) => Ok(pv.clone()),
942 PartialValue::Residual(e) => match e.expr_kind() {
943 ExprKind::Unknown(u) => self.unknown_to_partialvalue(u),
944 _ => Ok(pv.clone()),
945 },
946 })
947 .ok_or_else(|| {
948 EvaluationError::entity_attr_does_not_exist(
949 uid,
950 attr.clone(),
951 entity.keys(),
952 entity.get_tag(attr).is_some(),
953 entity.attrs_len(),
954 source_loc.cloned(),
955 )
956 })?,
957 },
958 PartialValue::Value(v) => {
959 #[allow(clippy::unwrap_used)]
961 Err(EvaluationError::type_error(
962 nonempty![
963 Type::Record,
964 Type::entity_type(names::ANY_ENTITY_TYPE.clone()),
965 ],
966 &v,
967 ))
968 }
969 }
970 }
971
972 fn short_circuit_residual_and_value(
975 &self,
976 e1: &Expr,
977 v2: &Value,
978 op: BinaryOp,
979 ) -> Option<PartialValue> {
980 match op {
981 BinaryOp::Add | BinaryOp::Eq | BinaryOp::Mul | BinaryOp::ContainsAny => {
983 self.short_circuit_value_and_residual(v2, e1, op)
984 }
985 _ => None,
986 }
987 }
988
989 fn short_circuit_value_and_residual(
992 &self,
993 v1: &Value,
994 e2: &Expr,
995 op: BinaryOp,
996 ) -> Option<PartialValue> {
997 match (op, v1.value_kind(), e2.expr_kind()) {
998 (
1000 BinaryOp::Eq,
1001 ValueKind::Lit(Literal::EntityUID(uid1)),
1002 ExprKind::Unknown(Unknown {
1003 type_annotation:
1004 Some(Type::Entity {
1005 ty: type_of_unknown,
1006 }),
1007 ..
1008 }),
1009 ) => {
1010 if uid1.entity_type() != type_of_unknown {
1011 Some(false.into())
1012 } else {
1013 None
1014 }
1015 }
1016 _ => None,
1017 }
1018 }
1019
1020 fn short_circuit_two_typed_residuals(
1021 &self,
1022 e1: &Expr,
1023 e2: &Expr,
1024 op: BinaryOp,
1025 ) -> Option<PartialValue> {
1026 match (op, e1.expr_kind(), e2.expr_kind()) {
1027 (
1029 BinaryOp::Eq,
1030 ExprKind::Unknown(Unknown {
1031 type_annotation: Some(Type::Entity { ty: t1 }),
1032 ..
1033 }),
1034 ExprKind::Unknown(Unknown {
1035 type_annotation: Some(Type::Entity { ty: t2 }),
1036 ..
1037 }),
1038 ) => {
1039 if t1 != t2 {
1040 Some(false.into())
1041 } else {
1042 None
1043 }
1044 }
1045 _ => None,
1046 }
1047 }
1048}
1049
1050#[cfg(test)]
1051impl Evaluator<'_> {
1052 pub fn interpret_inline_policy(&self, e: &Expr) -> Result<Value> {
1055 use std::collections::HashMap;
1056 match self.partial_interpret(e, &HashMap::new())? {
1057 PartialValue::Value(v) => {
1058 debug_assert!(e.source_loc().is_some() == v.source_loc().is_some());
1059 Ok(v)
1060 }
1061 PartialValue::Residual(r) => {
1062 debug_assert!(e.source_loc().is_some() == r.source_loc().is_some());
1063 Err(err::EvaluationError::non_value(r))
1064 }
1065 }
1066 }
1067
1068 pub fn partial_eval_expr(&self, p: &Expr) -> Result<Either<Value, Expr>> {
1070 let env = SlotEnv::new();
1071 match self.partial_interpret(p, &env)? {
1072 PartialValue::Value(v) => Ok(Either::Left(v)),
1073 PartialValue::Residual(r) => Ok(Either::Right(r)),
1074 }
1075 }
1076}
1077
1078impl std::fmt::Debug for Evaluator<'_> {
1079 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1080 write!(
1081 f,
1082 "<Evaluator with principal = {:?}, action = {:?}, resource = {:?}",
1083 &self.principal, &self.action, &self.resource
1084 )
1085 }
1086}
1087
1088impl Value {
1089 pub(crate) fn get_as_bool(&self) -> Result<bool> {
1092 match &self.value {
1093 ValueKind::Lit(Literal::Bool(b)) => Ok(*b),
1094 _ => Err(EvaluationError::type_error_single(Type::Bool, self)),
1095 }
1096 }
1097
1098 pub(crate) fn get_as_long(&self) -> Result<Integer> {
1101 match &self.value {
1102 ValueKind::Lit(Literal::Long(i)) => Ok(*i),
1103 _ => Err(EvaluationError::type_error_single(Type::Long, self)),
1104 }
1105 }
1106
1107 pub(crate) fn get_as_string(&self) -> Result<&SmolStr> {
1110 match &self.value {
1111 ValueKind::Lit(Literal::String(s)) => Ok(s),
1112 _ => Err(EvaluationError::type_error_single(Type::String, self)),
1113 }
1114 }
1115
1116 pub(crate) fn get_as_set(&self) -> Result<&Set> {
1118 match &self.value {
1119 ValueKind::Set(set) => Ok(set),
1120 _ => Err(EvaluationError::type_error_single(Type::Set, self)),
1121 }
1122 }
1123
1124 #[cfg(feature = "partial-eval")]
1126 pub(crate) fn get_as_record(&self) -> Result<&Arc<BTreeMap<SmolStr, Value>>> {
1127 match &self.value {
1128 ValueKind::Record(rec) => Ok(rec),
1129 _ => Err(EvaluationError::type_error_single(Type::Record, self)),
1130 }
1131 }
1132
1133 pub(crate) fn get_as_entity(&self) -> Result<&EntityUID> {
1136 match &self.value {
1137 ValueKind::Lit(Literal::EntityUID(uid)) => Ok(uid.as_ref()),
1138 _ => Err(EvaluationError::type_error_single(
1139 Type::entity_type(names::ANY_ENTITY_TYPE.clone()),
1140 self,
1141 )),
1142 }
1143 }
1144}
1145
1146#[inline(always)]
1147fn stack_size_check() -> Result<()> {
1148 if stacker::remaining_stack().unwrap_or(REQUIRED_STACK_SPACE) < REQUIRED_STACK_SPACE {
1150 return Err(EvaluationError::recursion_limit(None));
1151 }
1152 Ok(())
1153}
1154
1155#[allow(clippy::panic)]
1157#[allow(clippy::cognitive_complexity)]
1158#[cfg(test)]
1159pub(crate) mod test {
1160 use std::collections::{HashMap, HashSet};
1161 use std::str::FromStr;
1162
1163 use super::*;
1164
1165 use crate::{
1166 entities::{EntityJsonParser, NoEntitiesSchema, TCComputation},
1167 parser::{self, parse_expr, parse_policy_or_template, parse_policyset},
1168 test_utils::{expect_err, ExpectedErrorMessageBuilder},
1169 };
1170
1171 use cool_asserts::assert_matches;
1172
1173 pub fn basic_request() -> Request {
1175 Request::new(
1176 (EntityUID::with_eid("test_principal"), None),
1177 (EntityUID::with_eid("test_action"), None),
1178 (EntityUID::with_eid("test_resource"), None),
1179 Context::from_pairs(
1180 [
1181 ("cur_time".into(), RestrictedExpr::val("03:22:11")),
1182 (
1183 "device_properties".into(),
1184 RestrictedExpr::record(vec![
1185 ("os_name".into(), RestrictedExpr::val("Windows")),
1186 ("manufacturer".into(), RestrictedExpr::val("ACME Corp")),
1187 ])
1188 .unwrap(),
1189 ),
1190 ("violations".into(), RestrictedExpr::set([])),
1191 ],
1192 Extensions::none(),
1193 )
1194 .unwrap(),
1195 Some(&RequestSchemaAllPass),
1196 Extensions::none(),
1197 )
1198 .unwrap()
1199 }
1200
1201 pub fn basic_entities() -> Entities {
1203 Entities::from_entities(
1204 vec![
1205 Entity::with_uid(EntityUID::with_eid("foo")),
1206 Entity::with_uid(EntityUID::with_eid("test_principal")),
1207 Entity::with_uid(EntityUID::with_eid("test_action")),
1208 Entity::with_uid(EntityUID::with_eid("test_resource")),
1209 ],
1210 None::<&NoEntitiesSchema>,
1211 TCComputation::ComputeNow,
1212 Extensions::none(),
1213 )
1214 .expect("failed to create basic entities")
1215 }
1216
1217 pub fn rich_entities() -> Entities {
1219 let entity_no_attrs_no_parents =
1220 Entity::with_uid(EntityUID::with_eid("entity_no_attrs_no_parents"));
1221
1222 let attrs = HashMap::from([
1223 ("spoon".into(), RestrictedExpr::val(787)),
1224 ("fork".into(), RestrictedExpr::val("spoon")),
1225 (
1226 "tags".into(),
1227 RestrictedExpr::set(vec![
1228 RestrictedExpr::val("fun"),
1229 RestrictedExpr::val("good"),
1230 RestrictedExpr::val("useful"),
1231 ]),
1232 ),
1233 (
1234 "address".into(),
1235 RestrictedExpr::record(vec![
1236 ("street".into(), RestrictedExpr::val("234 magnolia")),
1237 ("town".into(), RestrictedExpr::val("barmstadt")),
1238 ("country".into(), RestrictedExpr::val("amazonia")),
1239 ])
1240 .unwrap(),
1241 ),
1242 ]);
1243 let entity_with_attrs = Entity::new(
1244 EntityUID::with_eid("entity_with_attrs"),
1245 attrs.clone(),
1246 HashSet::new(),
1247 HashSet::new(),
1248 HashMap::new(),
1249 Extensions::none(),
1250 )
1251 .unwrap();
1252
1253 let tags = HashMap::from([("spoon".into(), RestrictedExpr::val(-121))]);
1254 let entity_with_tags = Entity::new(
1255 EntityUID::with_eid("entity_with_tags"),
1256 HashMap::new(),
1257 HashSet::new(),
1258 HashSet::new(),
1259 tags.clone(),
1260 Extensions::none(),
1261 )
1262 .unwrap();
1263
1264 let entity_with_tags_and_attrs = Entity::new(
1265 EntityUID::with_eid("entity_with_tags_and_attrs"),
1266 attrs,
1267 HashSet::new(),
1268 HashSet::new(),
1269 tags,
1270 Extensions::none(),
1271 )
1272 .unwrap();
1273
1274 let mut child = Entity::with_uid(EntityUID::with_eid("child"));
1275 let mut parent = Entity::with_uid(EntityUID::with_eid("parent"));
1276 let grandparent = Entity::with_uid(EntityUID::with_eid("grandparent"));
1277 let mut sibling = Entity::with_uid(EntityUID::with_eid("sibling"));
1278 let unrelated = Entity::with_uid(EntityUID::with_eid("unrelated"));
1279 child.add_parent(parent.uid().clone());
1280 sibling.add_parent(parent.uid().clone());
1281 parent.add_parent(grandparent.uid().clone());
1282 let mut child_diff_type = Entity::with_uid(
1283 EntityUID::with_eid_and_type("other_type", "other_child")
1284 .expect("should be a valid identifier"),
1285 );
1286 child_diff_type.add_parent(parent.uid().clone());
1287 child_diff_type.add_indirect_ancestor(grandparent.uid().clone());
1288
1289 Entities::from_entities(
1290 vec![
1291 entity_no_attrs_no_parents,
1292 entity_with_attrs,
1293 entity_with_tags,
1294 entity_with_tags_and_attrs,
1295 child,
1296 child_diff_type,
1297 parent,
1298 grandparent,
1299 sibling,
1300 unrelated,
1301 ],
1302 None::<&NoEntitiesSchema>,
1303 TCComputation::ComputeNow,
1304 Extensions::all_available(),
1305 )
1306 .expect("Failed to create rich entities")
1307 }
1308
1309 #[cfg(feature = "partial-eval")]
1310 #[test]
1311 fn partial_entity_stores_in_set() {
1312 let q = basic_request();
1313 let entities = rich_entities().partial();
1314 let child = EntityUID::with_eid("child");
1315 let second = EntityUID::with_eid("joseph");
1316 let missing = EntityUID::with_eid("non-present");
1317 let parent = EntityUID::with_eid("parent");
1318 let eval = Evaluator::new(q, &entities, Extensions::none());
1319
1320 let e = Expr::binary_app(
1321 BinaryOp::In,
1322 Expr::val(child),
1323 Expr::set([Expr::val(parent.clone()), Expr::val(second.clone())]),
1324 );
1325 let r = eval.partial_eval_expr(&e).unwrap();
1326 assert_eq!(r, Either::Left(true.into()));
1327
1328 let e = Expr::binary_app(
1329 BinaryOp::In,
1330 Expr::val(missing.clone()),
1331 Expr::set([Expr::val(parent.clone()), Expr::val(second.clone())]),
1332 );
1333 let r = eval.partial_eval_expr(&e).unwrap();
1334 let expected_residual = Expr::binary_app(
1335 BinaryOp::In,
1336 Expr::unknown(Unknown::new_with_type(
1337 format!("{missing}"),
1338 Type::Entity {
1339 ty: EntityUID::test_entity_type(),
1340 },
1341 )),
1342 Expr::set([Expr::val(parent.clone()), Expr::val(second.clone())]),
1343 );
1344 let expected_residual2 = Expr::binary_app(
1345 BinaryOp::In,
1346 Expr::unknown(Unknown::new_with_type(
1347 format!("{missing}"),
1348 Type::Entity {
1349 ty: EntityUID::test_entity_type(),
1350 },
1351 )),
1352 Expr::set([Expr::val(second), Expr::val(parent)]),
1353 );
1354
1355 assert!(r == Either::Right(expected_residual) || r == Either::Right(expected_residual2));
1357 }
1358
1359 #[cfg(feature = "partial-eval")]
1360 #[test]
1361 fn partial_entity_stores_in() {
1362 let q = basic_request();
1363 let entities = rich_entities().partial();
1364 let child = EntityUID::with_eid("child");
1365 let missing = EntityUID::with_eid("non-present");
1366 let parent = EntityUID::with_eid("parent");
1367 let eval = Evaluator::new(q, &entities, Extensions::none());
1368
1369 let e = Expr::binary_app(BinaryOp::In, Expr::val(child), Expr::val(parent.clone()));
1370 let r = eval.partial_eval_expr(&e).unwrap();
1371 assert_eq!(r, Either::Left(true.into()));
1372
1373 let e = Expr::binary_app(
1374 BinaryOp::In,
1375 Expr::val(missing.clone()),
1376 Expr::val(parent.clone()),
1377 );
1378 let r = eval.partial_eval_expr(&e).unwrap();
1379 let expected_residual = Expr::binary_app(
1380 BinaryOp::In,
1381 Expr::unknown(Unknown::new_with_type(
1382 format!("{missing}"),
1383 Type::Entity {
1384 ty: EntityUID::test_entity_type(),
1385 },
1386 )),
1387 Expr::val(parent),
1388 );
1389 assert_eq!(r, Either::Right(expected_residual));
1390 }
1391
1392 #[cfg(feature = "partial-eval")]
1393 #[test]
1394 fn partial_entity_stores_hasattr() {
1395 let q = basic_request();
1396 let entities = rich_entities().partial();
1397 let has_attr = EntityUID::with_eid("entity_with_attrs");
1398 let missing = EntityUID::with_eid("missing");
1399 let eval = Evaluator::new(q, &entities, Extensions::none());
1400
1401 let e = Expr::has_attr(Expr::val(has_attr), "spoon".into());
1402 let r = eval.partial_eval_expr(&e).unwrap();
1403 assert_eq!(r, Either::Left(true.into()));
1404
1405 let e = Expr::has_attr(Expr::val(missing.clone()), "spoon".into());
1406 let r = eval.partial_eval_expr(&e).unwrap();
1407 let expected_residual = Expr::has_attr(
1408 Expr::unknown(Unknown::new_with_type(
1409 format!("{missing}"),
1410 Type::Entity {
1411 ty: EntityUID::test_entity_type(),
1412 },
1413 )),
1414 "spoon".into(),
1415 );
1416 assert_eq!(r, Either::Right(expected_residual));
1417 }
1418
1419 #[cfg(feature = "partial-eval")]
1420 #[test]
1421 fn partial_entity_stores_getattr() {
1422 let q = basic_request();
1423 let entities = rich_entities().partial();
1424 let has_attr = EntityUID::with_eid("entity_with_attrs");
1425 let missing = EntityUID::with_eid("missing");
1426 let eval = Evaluator::new(q, &entities, Extensions::none());
1427
1428 let e = Expr::get_attr(Expr::val(has_attr), "spoon".into());
1429 let r = eval.partial_eval_expr(&e).unwrap();
1430 assert_eq!(r, Either::Left(787.into()));
1431
1432 let e = Expr::get_attr(Expr::val(missing.clone()), "spoon".into());
1433 let r = eval.partial_eval_expr(&e).unwrap();
1434 let expected_residual = Expr::get_attr(
1435 Expr::unknown(Unknown::new_with_type(
1436 format!("{missing}"),
1437 Type::Entity {
1438 ty: EntityUID::test_entity_type(),
1439 },
1440 )),
1441 "spoon".into(),
1442 );
1443 assert_eq!(r, Either::Right(expected_residual));
1444 }
1445
1446 #[test]
1447 fn interpret_primitives() {
1448 let request = basic_request();
1449 let entities = basic_entities();
1450 let eval = Evaluator::new(request, &entities, Extensions::none());
1451 assert_eq!(
1457 eval.interpret_inline_policy(&Expr::val(false)),
1458 Ok(Value {
1459 value: ValueKind::Lit(Literal::Bool(false)),
1460 loc: None,
1461 }),
1462 );
1463 assert_eq!(
1464 eval.interpret_inline_policy(&Expr::val(true)),
1465 Ok(Value {
1466 value: ValueKind::Lit(Literal::Bool(true)),
1467 loc: None,
1468 }),
1469 );
1470 assert_eq!(
1471 eval.interpret_inline_policy(&Expr::val(57)),
1472 Ok(Value {
1473 value: ValueKind::Lit(Literal::Long(57)),
1474 loc: None,
1475 }),
1476 );
1477 assert_eq!(
1478 eval.interpret_inline_policy(&Expr::val(-3)),
1479 Ok(Value {
1480 value: ValueKind::Lit(Literal::Long(-3)),
1481 loc: None,
1482 }),
1483 );
1484 assert_eq!(
1485 eval.interpret_inline_policy(&Expr::val("")),
1486 Ok(Value {
1487 value: ValueKind::Lit(Literal::String("".into())),
1488 loc: None,
1489 }),
1490 );
1491 assert_eq!(
1492 eval.interpret_inline_policy(&Expr::val("Hello")),
1493 Ok(Value {
1494 value: ValueKind::Lit(Literal::String("Hello".into())),
1495 loc: None,
1496 }),
1497 );
1498 }
1499
1500 #[test]
1501 fn interpret_entities() {
1502 let request = basic_request();
1503 let entities = basic_entities();
1504 let eval = Evaluator::new(request, &entities, Extensions::none());
1505 assert_eq!(
1511 eval.interpret_inline_policy(&Expr::val(EntityUID::with_eid("foo"))),
1512 Ok(Value {
1513 value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid("foo")))),
1514 loc: None,
1515 }),
1516 );
1517 assert_eq!(
1520 eval.interpret_inline_policy(&Expr::val(EntityUID::with_eid("doesnotexist"))),
1521 Ok(Value {
1522 value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid(
1523 "doesnotexist"
1524 )))),
1525 loc: None,
1526 }),
1527 );
1528 }
1529
1530 #[test]
1531 fn interpret_builtin_vars() {
1532 let request = basic_request();
1533 let entities = basic_entities();
1534 let eval = Evaluator::new(request, &entities, Extensions::none());
1535 assert_eq!(
1536 eval.interpret_inline_policy(&Expr::var(Var::Principal)),
1537 Ok(Value::from(EntityUID::with_eid("test_principal")))
1538 );
1539 assert_eq!(
1540 eval.interpret_inline_policy(&Expr::var(Var::Action)),
1541 Ok(Value::from(EntityUID::with_eid("test_action")))
1542 );
1543 assert_eq!(
1544 eval.interpret_inline_policy(&Expr::var(Var::Resource)),
1545 Ok(Value::from(EntityUID::with_eid("test_resource")))
1546 );
1547 }
1548
1549 #[test]
1550 fn interpret_entity_attrs() {
1551 let request = basic_request();
1552 let entities = rich_entities();
1553 let eval = Evaluator::new(request, &entities, Extensions::none());
1554 assert_eq!(
1556 eval.interpret_inline_policy(&Expr::has_attr(
1557 Expr::val(EntityUID::with_eid("entity_no_attrs_no_parents")),
1558 "doesnotexist".into()
1559 )),
1560 Ok(Value::from(false))
1561 );
1562 assert_eq!(
1564 eval.interpret_inline_policy(&Expr::has_attr(
1565 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1566 "doesnotexist".into()
1567 )),
1568 Ok(Value::from(false))
1569 );
1570 assert_eq!(
1572 eval.interpret_inline_policy(&Expr::has_attr(
1573 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1574 "tags".into()
1575 )),
1576 Ok(Value::from(true))
1577 );
1578 assert_matches!(
1580 eval.interpret_inline_policy(&Expr::get_attr(
1581 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1582 "doesnotexist".into()
1583 )),
1584 Err(EvaluationError::EntityAttrDoesNotExist(e)) => {
1585 let report = miette::Report::new(e.clone());
1586 assert_eq!(e.entity.as_ref(), &EntityUID::with_eid("entity_with_attrs"));
1587 assert_eq!(&e.attr_or_tag, "doesnotexist");
1588 let available_attrs = e.available_attrs_or_tags;
1589 assert_eq!(available_attrs.len(), 4);
1590 assert!(available_attrs.contains(&"spoon".into()));
1591 assert!(available_attrs.contains(&"address".into()));
1592 assert!(available_attrs.contains(&"tags".into()));
1593 expect_err(
1594 "",
1595 &report,
1596 &ExpectedErrorMessageBuilder::error(r#"`test_entity_type::"entity_with_attrs"` does not have the attribute `doesnotexist`"#)
1597 .help("available attributes: [address,fork,spoon,tags]")
1598 .build()
1599 );
1600 }
1601 );
1602 assert_matches!(
1604 eval.interpret_inline_policy(&Expr::get_attr(
1605 Expr::val(EntityUID::with_eid("entity_with_tags")),
1606 "spoon".into()
1607 )),
1608 Err(EvaluationError::EntityAttrDoesNotExist(e)) => {
1609 let report = miette::Report::new(e.clone());
1610 assert_eq!(e.entity.as_ref(), &EntityUID::with_eid("entity_with_tags"));
1611 assert_eq!(&e.attr_or_tag, "spoon");
1612 let available_attrs = e.available_attrs_or_tags;
1613 assert_eq!(available_attrs.len(), 0);
1614 let expected_error_message =
1615 ExpectedErrorMessageBuilder::error(r#"`test_entity_type::"entity_with_tags"` does not have the attribute `spoon`"#)
1616 .help(r#"`test_entity_type::"entity_with_tags"` does not have any attributes; note that a tag (not an attribute) named `spoon` does exist"#)
1617 .build();
1618 expect_err("", &report, &expected_error_message);
1619 }
1620 );
1621 assert_eq!(
1623 eval.interpret_inline_policy(&Expr::get_attr(
1624 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1625 "spoon".into()
1626 )),
1627 Ok(Value::from(787))
1628 );
1629 assert_eq!(
1631 eval.interpret_inline_policy(&Expr::contains(
1632 Expr::get_attr(
1633 Expr::val(EntityUID::with_eid("entity_with_tags_and_attrs")),
1634 "tags".into()
1635 ),
1636 Expr::val("useful")
1637 )),
1638 Ok(Value::from(true))
1639 );
1640 assert_eq!(
1642 eval.interpret_inline_policy(&Expr::has_attr(
1643 Expr::val(EntityUID::with_eid("doesnotexist")),
1644 "foo".into()
1645 )),
1646 Ok(Value::from(false))
1647 );
1648 assert_eq!(
1650 eval.interpret_inline_policy(&Expr::get_attr(
1651 Expr::val(EntityUID::with_eid("doesnotexist")),
1652 "foo".into()
1653 )),
1654 Err(EvaluationError::entity_does_not_exist(
1655 Arc::new(EntityUID::with_eid("doesnotexist")),
1656 None
1657 ))
1658 );
1659 }
1660
1661 #[test]
1662 fn interpret_entity_tags() {
1663 let request = basic_request();
1664 let entities = rich_entities();
1665 let eval = Evaluator::new(request, &entities, Extensions::none());
1666 assert_eq!(
1668 eval.interpret_inline_policy(&Expr::has_tag(
1669 Expr::val(EntityUID::with_eid("entity_no_attrs_no_parents")),
1670 Expr::val("doesnotexist"),
1671 )),
1672 Ok(Value::from(false))
1673 );
1674 assert_eq!(
1676 eval.interpret_inline_policy(&Expr::has_tag(
1677 Expr::val(EntityUID::with_eid("entity_with_tags")),
1678 Expr::val("doesnotexist"),
1679 )),
1680 Ok(Value::from(false))
1681 );
1682 assert_eq!(
1684 eval.interpret_inline_policy(&Expr::has_tag(
1685 Expr::val(EntityUID::with_eid("entity_with_tags_and_attrs")),
1686 Expr::val("address"),
1687 )),
1688 Ok(Value::from(false))
1689 );
1690 assert_eq!(
1692 eval.interpret_inline_policy(&Expr::has_tag(
1693 Expr::val(EntityUID::with_eid("entity_with_tags")),
1694 Expr::val("spoon"),
1695 )),
1696 Ok(Value::from(true))
1697 );
1698 assert_eq!(
1700 eval.interpret_inline_policy(&Expr::has_tag(
1701 Expr::val(EntityUID::with_eid("entity_with_tags")),
1702 Expr::get_attr(
1703 Expr::val(EntityUID::with_eid("entity_with_tags_and_attrs")),
1704 "fork".into()
1705 ),
1706 )),
1707 Ok(Value::from(true))
1708 );
1709 assert_matches!(
1711 eval.interpret_inline_policy(&Expr::get_tag(
1712 Expr::val(EntityUID::with_eid("entity_with_tags")),
1713 Expr::val("doesnotexist"),
1714 )),
1715 Err(EvaluationError::EntityAttrDoesNotExist(e)) => {
1716 let report = miette::Report::new(e.clone());
1717 assert_eq!(e.entity.as_ref(), &EntityUID::with_eid("entity_with_tags"));
1718 assert_eq!(&e.attr_or_tag, "doesnotexist");
1719 let available_attrs = e.available_attrs_or_tags;
1720 assert_eq!(available_attrs.len(), 1);
1721 assert!(available_attrs.contains(&"spoon".into()));
1722 expect_err(
1723 "",
1724 &report,
1725 &ExpectedErrorMessageBuilder::error(r#"`test_entity_type::"entity_with_tags"` does not have the tag `doesnotexist`"#)
1726 .help("available tags: [spoon]")
1727 .build()
1728 );
1729 }
1730 );
1731 assert_matches!(
1733 eval.interpret_inline_policy(&Expr::get_tag(
1734 Expr::val(EntityUID::with_eid("entity_with_tags_and_attrs")),
1735 Expr::val("address"),
1736 )),
1737 Err(EvaluationError::EntityAttrDoesNotExist(e)) => {
1738 let report = miette::Report::new(e.clone());
1739 assert_eq!(e.entity.as_ref(), &EntityUID::with_eid("entity_with_tags_and_attrs"));
1740 assert_eq!(&e.attr_or_tag, "address");
1741 let available_attrs = e.available_attrs_or_tags;
1742 assert_eq!(available_attrs.len(), 1);
1743 assert!(available_attrs.contains(&"spoon".into()));
1744 expect_err(
1745 "",
1746 &report,
1747 &ExpectedErrorMessageBuilder::error(r#"`test_entity_type::"entity_with_tags_and_attrs"` does not have the tag `address`"#)
1748 .help("available tags: [spoon]; note that an attribute (not a tag) named `address` does exist")
1749 .build()
1750 );
1751 }
1752 );
1753 assert_eq!(
1755 eval.interpret_inline_policy(&Expr::get_tag(
1756 Expr::val(EntityUID::with_eid("entity_with_tags")),
1757 Expr::val("spoon"),
1758 )),
1759 Ok(Value::from(-121))
1760 );
1761 assert_eq!(
1763 eval.interpret_inline_policy(&Expr::get_tag(
1764 Expr::val(EntityUID::with_eid("entity_with_tags")),
1765 Expr::get_attr(
1766 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1767 "fork".into()
1768 ),
1769 )),
1770 Ok(Value::from(-121))
1771 );
1772 assert_matches!(
1774 eval.interpret_inline_policy(&Expr::get_tag(
1775 Expr::val(EntityUID::with_eid("entity_with_tags")),
1776 Expr::get_attr(
1777 Expr::get_attr(
1778 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1779 "address".into()
1780 ),
1781 "country".into()
1782 ),
1783 )),
1784 Err(e) => {
1785 expect_err(
1786 "",
1787 &miette::Report::new(e),
1788 &ExpectedErrorMessageBuilder::error(r#"`test_entity_type::"entity_with_tags"` does not have the tag `amazonia`"#)
1789 .help("available tags: [spoon]")
1790 .build(),
1791 )
1792 }
1793 );
1794 assert_eq!(
1796 eval.interpret_inline_policy(&Expr::has_tag(
1797 Expr::val(EntityUID::with_eid("doesnotexist")),
1798 Expr::val("foo"),
1799 )),
1800 Ok(Value::from(false))
1801 );
1802 assert_eq!(
1804 eval.interpret_inline_policy(&Expr::get_tag(
1805 Expr::val(EntityUID::with_eid("doesnotexist")),
1806 Expr::val("foo"),
1807 )),
1808 Err(EvaluationError::entity_does_not_exist(
1809 Arc::new(EntityUID::with_eid("doesnotexist")),
1810 None
1811 ))
1812 );
1813 assert_matches!(
1815 eval.interpret_inline_policy(&Expr::get_tag(
1816 Expr::record([
1817 ("spoon".into(), Expr::val(78)),
1818 ]).unwrap(),
1819 Expr::val("spoon"),
1820 )),
1821 Err(e) => {
1822 expect_err(
1823 "",
1824 &miette::Report::new(e),
1825 &ExpectedErrorMessageBuilder::error("type error: expected (entity of type `any_entity_type`), got record")
1826 .build()
1827 );
1828 }
1829 );
1830 assert_matches!(
1832 eval.interpret_inline_policy(&Expr::has_tag(
1833 Expr::record([
1834 ("spoon".into(), Expr::val(78)),
1835 ]).unwrap(),
1836 Expr::val("spoon"),
1837 )),
1838 Err(e) => {
1839 expect_err(
1840 "",
1841 &miette::Report::new(e),
1842 &ExpectedErrorMessageBuilder::error("type error: expected (entity of type `any_entity_type`), got record")
1843 .build()
1844 );
1845 }
1846 );
1847 assert_matches!(
1849 eval.interpret_inline_policy(&Expr::get_tag(
1850 Expr::val(EntityUID::with_eid("entity_with_tags")),
1851 Expr::get_attr(Expr::val(EntityUID::with_eid("entity_with_attrs")), "spoon".into()),
1852 )),
1853 Err(e) => {
1854 expect_err(
1855 "",
1856 &miette::Report::new(e),
1857 &ExpectedErrorMessageBuilder::error("type error: expected string, got long")
1858 .build()
1859 );
1860 }
1861 );
1862 assert_matches!(
1864 eval.interpret_inline_policy(&Expr::has_tag(
1865 Expr::val(EntityUID::with_eid("entity_with_tags")),
1866 Expr::get_attr(Expr::val(EntityUID::with_eid("entity_with_attrs")), "spoon".into()),
1867 )),
1868 Err(e) => {
1869 expect_err(
1870 "",
1871 &miette::Report::new(e),
1872 &ExpectedErrorMessageBuilder::error("type error: expected string, got long")
1873 .build()
1874 );
1875 }
1876 );
1877 }
1878
1879 #[test]
1880 fn interpret_ternaries() {
1881 let request = basic_request();
1882 let entities = basic_entities();
1883 let eval = Evaluator::new(request, &entities, Extensions::none());
1884 assert_eq!(
1886 eval.interpret_inline_policy(&Expr::ite(Expr::val(true), Expr::val(3), Expr::val(8))),
1887 Ok(Value::from(3))
1888 );
1889 assert_eq!(
1891 eval.interpret_inline_policy(&Expr::ite(Expr::val(false), Expr::val(3), Expr::val(8))),
1892 Ok(Value::from(8))
1893 );
1894 assert_eq!(
1896 eval.interpret_inline_policy(&Expr::ite(
1897 Expr::val(false),
1898 Expr::val(false),
1899 Expr::val(true)
1900 )),
1901 Ok(Value::from(true))
1902 );
1903 assert_eq!(
1905 eval.interpret_inline_policy(&Expr::ite(
1906 Expr::val(false),
1907 Expr::var(Var::Principal),
1908 Expr::var(Var::Resource)
1909 )),
1910 Ok(Value::from(EntityUID::with_eid("test_resource")))
1911 );
1912 assert_matches!(
1914 eval.interpret_inline_policy(&Expr::ite(
1915 Expr::val("hello"),
1916 Expr::val(3),
1917 Expr::val(8)
1918 )),
1919 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1920 assert_eq!(expected, nonempty![Type::Bool]);
1921 assert_eq!(actual, Type::String);
1922 assert_eq!(advice, None);
1923 }
1924 );
1925 assert_matches!(
1927 eval.interpret_inline_policy(&Expr::ite(
1928 Expr::var(Var::Principal),
1929 Expr::val(3),
1930 Expr::val(8)
1931 )),
1932 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1933 assert_eq!(expected, nonempty![Type::Bool]);
1934 assert_eq!(actual, Type::Entity {
1935 ty: EntityUID::test_entity_type(),
1936 });
1937 assert_eq!(advice, None);
1938 }
1939 );
1940 assert_eq!(
1942 eval.interpret_inline_policy(&Expr::ite(
1943 Expr::val(true),
1944 Expr::val("hello"),
1945 Expr::val(2)
1946 )),
1947 Ok(Value::from("hello"))
1948 );
1949 assert_eq!(
1951 eval.interpret_inline_policy(&Expr::ite(
1952 Expr::val(false),
1953 Expr::val("hello"),
1954 Expr::val(2)
1955 )),
1956 Ok(Value::from(2))
1957 );
1958 assert_eq!(
1960 eval.interpret_inline_policy(&Expr::ite(
1961 Expr::val(true),
1962 Expr::ite(Expr::val(true), Expr::val(3), Expr::val(8)),
1963 Expr::val(-10)
1964 )),
1965 Ok(Value::from(3))
1966 );
1967 assert_eq!(
1969 eval.interpret_inline_policy(&Expr::ite(
1970 Expr::val(true),
1971 Expr::ite(Expr::val(false), Expr::val(3), Expr::val(8)),
1972 Expr::val(-10)
1973 )),
1974 Ok(Value::from(8))
1975 );
1976 assert_eq!(
1978 eval.interpret_inline_policy(&Expr::ite(
1979 Expr::val(false),
1980 Expr::ite(Expr::val(false), Expr::val(3), Expr::val(8)),
1981 Expr::val(-10)
1982 )),
1983 Ok(Value::from(-10))
1984 );
1985 assert_eq!(
1987 eval.interpret_inline_policy(&Expr::ite(
1988 Expr::val(false),
1989 Expr::ite(Expr::val("hello"), Expr::val(3), Expr::val(8)),
1990 Expr::val(-10)
1991 )),
1992 Ok(Value::from(-10))
1993 );
1994 assert_eq!(
1996 eval.interpret_inline_policy(&Expr::ite(
1997 Expr::val(true),
1998 Expr::val(3),
1999 Expr::ite(Expr::val(true), Expr::val(8), Expr::val(-10))
2000 )),
2001 Ok(Value::from(3))
2002 );
2003 assert_eq!(
2005 eval.interpret_inline_policy(&Expr::ite(
2006 Expr::ite(Expr::val(true), Expr::val(false), Expr::val(true)),
2007 Expr::val(3),
2008 Expr::val(8)
2009 )),
2010 Ok(Value::from(8))
2011 );
2012 assert_eq!(
2014 eval.interpret_inline_policy(&Expr::ite(
2015 Expr::val(true),
2016 Expr::val(3),
2017 Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
2018 )),
2019 Ok(Value::from(3))
2020 );
2021 assert_eq!(
2023 eval.interpret_inline_policy(&Expr::ite(
2024 Expr::val(false),
2025 Expr::val(3),
2026 Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
2027 )),
2028 Err(EvaluationError::record_attr_does_not_exist(
2029 "foo".into(),
2030 std::iter::empty(),
2031 0,
2032 None,
2033 ))
2034 );
2035 assert_eq!(
2037 eval.interpret_inline_policy(&Expr::ite(
2038 Expr::val(true),
2039 Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
2040 Expr::val(3),
2041 )),
2042 Err(EvaluationError::record_attr_does_not_exist(
2043 "foo".into(),
2044 std::iter::empty(),
2045 0,
2046 None,
2047 ))
2048 );
2049 assert_eq!(
2051 eval.interpret_inline_policy(&Expr::ite(
2052 Expr::val(false),
2053 Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
2054 Expr::val(3),
2055 )),
2056 Ok(Value::from(3))
2057 );
2058 }
2059
2060 #[test]
2061 fn interpret_sets() {
2062 let request = basic_request();
2063 let entities = basic_entities();
2064 let eval = Evaluator::new(request, &entities, Extensions::none());
2065 assert_eq!(
2073 eval.interpret_inline_policy(&Expr::set(vec![Expr::val(8)])),
2074 Ok(Value::set(
2075 vec![Value {
2076 value: ValueKind::Lit(Literal::Long(8)),
2077 loc: None,
2078 }],
2079 None,
2080 )),
2081 );
2082 assert_eq!(
2084 eval.interpret_inline_policy(&Expr::set(vec![
2085 Expr::val(8),
2086 Expr::val(2),
2087 Expr::val(101),
2088 ])),
2089 Ok(Value::set(
2090 vec![
2091 Value {
2092 value: ValueKind::Lit(Literal::Long(8)),
2093 loc: None,
2094 },
2095 Value {
2096 value: ValueKind::Lit(Literal::Long(2)),
2097 loc: None,
2098 },
2099 Value {
2100 value: ValueKind::Lit(Literal::Long(101)),
2101 loc: None,
2102 },
2103 ],
2104 None,
2105 )),
2106 );
2107 assert_eq!(
2109 eval.interpret_inline_policy(&Expr::set(vec![])),
2110 Ok(Value::empty_set(None)),
2111 );
2112 assert_eq!(
2113 eval.interpret_inline_policy(&Expr::set(vec![])),
2114 Ok(Value::empty_set(None)),
2115 );
2116 assert_matches!(
2118 eval.interpret_inline_policy(&Expr::get_attr(
2119 Expr::set(vec![Expr::val(8)]),
2120 "hello".into()
2121 )),
2122 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2123 assert_eq!(expected, nonempty![
2124 Type::Record,
2125 Type::entity_type(
2126 Name::parse_unqualified_name("any_entity_type")
2127 .expect("should be a valid identifier")
2128 ),
2129 ]);
2130 assert_eq!(actual, Type::Set);
2131 assert_eq!(advice, None);
2132 }
2133 );
2134 assert_matches!(
2136 eval.interpret_inline_policy(&Expr::get_attr(Expr::set(vec![]), "hello".into())),
2137 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2138 assert_eq!(expected, nonempty![
2139 Type::Record,
2140 Type::entity_type(
2141 Name::parse_unqualified_name("any_entity_type")
2142 .expect("should be a valid identifier")
2143 ),
2144 ]);
2145 assert_eq!(actual, Type::Set);
2146 assert_eq!(advice, None);
2147 }
2148 );
2149 let mixed_set = Expr::set(vec![
2151 Expr::val("hello"),
2152 Expr::val(2),
2153 Expr::val(true),
2154 Expr::val(EntityUID::with_eid("foo")),
2155 ]);
2156 assert_eq!(
2157 eval.interpret_inline_policy(&mixed_set),
2158 Ok(Value::set(
2159 vec![
2160 Value {
2161 value: ValueKind::Lit(Literal::String("hello".into())),
2162 loc: None,
2163 },
2164 Value {
2165 value: ValueKind::Lit(Literal::Long(2)),
2166 loc: None,
2167 },
2168 Value {
2169 value: ValueKind::Lit(Literal::Bool(true)),
2170 loc: None,
2171 },
2172 Value {
2173 value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid(
2174 "foo"
2175 )))),
2176 loc: None,
2177 },
2178 ],
2179 None,
2180 )),
2181 );
2182 assert_matches!(
2184 eval.interpret_inline_policy(&Expr::get_attr(mixed_set, "hello".into())),
2185 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2186 assert_eq!(expected, nonempty![
2187 Type::Record,
2188 Type::entity_type(
2189 Name::parse_unqualified_name("any_entity_type")
2190 .expect("should be a valid identifier")
2191 ),
2192 ]);
2193 assert_eq!(actual, Type::Set);
2194 assert_eq!(advice, None);
2195 }
2196 );
2197 let set_of_sets = Expr::set(vec![
2199 Expr::set(vec![Expr::val(8), Expr::val(2)]),
2200 Expr::set(vec![Expr::val(13), Expr::val(702)]),
2201 Expr::set(vec![Expr::val(3)]),
2202 ]);
2203 assert_eq!(
2204 eval.interpret_inline_policy(&set_of_sets),
2205 Ok(Value::set(
2206 vec![
2207 Value::set(
2208 vec![
2209 Value {
2210 value: ValueKind::Lit(Literal::Long(8)),
2211 loc: None,
2212 },
2213 Value {
2214 value: ValueKind::Lit(Literal::Long(2)),
2215 loc: None,
2216 },
2217 ],
2218 None,
2219 ),
2220 Value::set(
2221 vec![
2222 Value {
2223 value: ValueKind::Lit(Literal::Long(13)),
2224 loc: None,
2225 },
2226 Value {
2227 value: ValueKind::Lit(Literal::Long(702)),
2228 loc: None,
2229 },
2230 ],
2231 None,
2232 ),
2233 Value::set(
2234 vec![Value {
2235 value: ValueKind::Lit(Literal::Long(3)),
2236 loc: None,
2237 }],
2238 None,
2239 ),
2240 ],
2241 None,
2242 )),
2243 );
2244 assert_matches!(
2246 eval.interpret_inline_policy(&Expr::get_attr(set_of_sets.clone(), "hello".into())),
2247 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2248 assert_eq!(expected, nonempty![
2249 Type::Record,
2250 Type::entity_type(
2251 Name::parse_unqualified_name("any_entity_type")
2252 .expect("should be a valid identifier")
2253 ),
2254 ]);
2255 assert_eq!(actual, Type::Set);
2256 assert_eq!(advice, None);
2257 }
2258 );
2259 assert_matches!(
2261 eval.interpret_inline_policy(&Expr::get_attr(
2262 Expr::get_attr(set_of_sets, "ham".into()),
2263 "eggs".into()
2264 )),
2265 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2266 assert_eq!(expected, nonempty![
2267 Type::Record,
2268 Type::entity_type(
2269 Name::parse_unqualified_name("any_entity_type")
2270 .expect("should be a valid identifier")
2271 ),
2272 ]);
2273 assert_eq!(actual, Type::Set);
2274 assert_eq!(advice, None);
2275 }
2276 );
2277 }
2278
2279 #[test]
2280 fn interpret_records() {
2281 let request = basic_request();
2282 let entities = rich_entities();
2283 let eval = Evaluator::new(request, &entities, Extensions::none());
2284 let string_key = Expr::record(vec![("key".into(), Expr::val(3))]).unwrap();
2286 assert_eq!(
2287 eval.interpret_inline_policy(&Expr::get_attr(string_key, "key".into())),
2288 Ok(Value::from(3))
2289 );
2290 let ham_and_eggs = Expr::record(vec![
2292 ("ham".into(), Expr::val(3)),
2293 ("eggs".into(), Expr::val(7)),
2294 ])
2295 .unwrap();
2296 assert_eq!(
2297 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs.clone(), "ham".into())),
2298 Ok(Value::from(3))
2299 );
2300 assert_eq!(
2302 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs.clone(), "eggs".into())),
2303 Ok(Value::from(7))
2304 );
2305 assert_eq!(
2307 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs, "what".into())),
2308 Err(EvaluationError::record_attr_does_not_exist(
2309 "what".into(),
2310 [&"eggs".into(), &"ham".into()],
2311 2,
2312 None,
2313 ))
2314 );
2315
2316 let ham_and_eggs_2 = Expr::record(vec![
2318 ("ham".into(), Expr::val(3)),
2319 ("eggs".into(), Expr::val("why")),
2320 ])
2321 .unwrap();
2322 assert_eq!(
2323 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs_2.clone(), "ham".into())),
2324 Ok(Value::from(3))
2325 );
2326 assert_eq!(
2328 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs_2, "eggs".into())),
2329 Ok(Value::from("why"))
2330 );
2331 let ham_and_eggs_3 = Expr::record(vec![
2333 ("ham".into(), Expr::val(3)),
2334 ("eggs".into(), Expr::val("why")),
2335 ("else".into(), Expr::val(EntityUID::with_eid("foo"))),
2336 ])
2337 .unwrap();
2338 assert_eq!(
2339 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs_3, "else".into())),
2340 Ok(Value::from(EntityUID::with_eid("foo")))
2341 );
2342 let hams_and_eggs = Expr::record(vec![
2344 (
2345 "hams".into(),
2346 Expr::record(vec![
2347 ("some".into(), Expr::val(1)),
2348 ("more".into(), Expr::val(2)),
2349 ])
2350 .unwrap(),
2351 ),
2352 ("eggs".into(), Expr::val("why")),
2353 ])
2354 .unwrap();
2355 assert_eq!(
2356 eval.interpret_inline_policy(&Expr::get_attr(
2357 Expr::get_attr(hams_and_eggs, "hams".into()),
2358 "more".into()
2359 )),
2360 Ok(Value::from(2))
2361 );
2362 let weird_key = Expr::record(vec![(
2364 "this is a valid map key+.-_%() ".into(),
2365 Expr::val(7),
2366 )])
2367 .unwrap();
2368 assert_eq!(
2369 eval.interpret_inline_policy(&Expr::get_attr(
2370 weird_key,
2371 "this is a valid map key+.-_%() ".into()
2372 )),
2373 Ok(Value::from(7))
2374 );
2375 assert_eq!(
2377 eval.interpret_inline_policy(&Expr::get_attr(
2378 Expr::record(vec![
2379 ("foo".into(), Expr::val(2)),
2380 (
2381 "bar".into(),
2382 Expr::set(vec![Expr::val(3), Expr::val(33), Expr::val(333)])
2383 )
2384 ])
2385 .unwrap(),
2386 "bar".into()
2387 )),
2388 Ok(Value::set(
2389 vec![Value::from(3), Value::from(33), Value::from(333)],
2390 None
2391 ))
2392 );
2393 assert_eq!(
2395 eval.interpret_inline_policy(&Expr::get_attr(
2396 Expr::get_attr(
2397 Expr::record(vec![
2398 ("foo".into(), Expr::val(2)),
2399 (
2400 "bar".into(),
2401 Expr::record(vec![
2402 ("a+b".into(), Expr::val(5)),
2403 ("jkl;".into(), Expr::val(10)),
2404 ])
2405 .unwrap()
2406 ),
2407 ])
2408 .unwrap(),
2409 "bar".into()
2410 ),
2411 "a+b".into()
2412 )),
2413 Ok(Value::from(5))
2414 );
2415 assert_eq!(
2417 eval.interpret_inline_policy(&Expr::get_attr(
2418 Expr::get_attr(
2419 Expr::record(vec![
2420 ("foo".into(), Expr::val(2)),
2421 (
2422 "bar".into(),
2423 Expr::record(vec![
2424 ("foo".into(), Expr::val(4)),
2425 ("cake".into(), Expr::val(77)),
2426 ])
2427 .unwrap()
2428 ),
2429 ])
2430 .unwrap(),
2431 "bar".into(),
2432 ),
2433 "foo".into(),
2434 )),
2435 Ok(Value::from(4))
2436 );
2437 assert_eq!(
2440 Expr::record(vec![
2441 ("foo".into(), Expr::val(2)),
2442 ("bar".into(), Expr::val(4)),
2443 ("foo".into(), Expr::val("hi")),
2444 ]),
2445 Err(expression_construction_errors::DuplicateKeyError {
2446 key: "foo".into(),
2447 context: "in record literal",
2448 }
2449 .into())
2450 );
2451 assert_eq!(
2453 eval.interpret_inline_policy(&Expr::get_attr(
2454 Expr::get_attr(
2455 Expr::val(EntityUID::with_eid("entity_with_attrs")),
2456 "address".into()
2457 ),
2458 "street".into()
2459 )),
2460 Ok(Value::from("234 magnolia"))
2461 );
2462 assert_eq!(
2464 eval.interpret_inline_policy(&Expr::get_attr(
2465 Expr::var(Var::Context),
2466 "cur_time".into()
2467 )),
2468 Ok(Value::from("03:22:11"))
2469 );
2470 assert_eq!(
2472 eval.interpret_inline_policy(&Expr::get_attr(
2473 Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2474 "os_name".into()
2475 )),
2476 Ok(Value::from("Windows"))
2477 );
2478 assert_eq!(
2481 eval.interpret_inline_policy(&Expr::has_attr(
2482 Expr::record(vec![
2483 ("foo".into(), Expr::val(77)),
2484 ("bar".into(), Expr::val("pancakes")),
2485 ])
2486 .unwrap(),
2487 "foo".into()
2488 )),
2489 Ok(Value::from(true))
2490 );
2491 assert_eq!(
2494 eval.interpret_inline_policy(&Expr::has_attr(
2495 Expr::record(vec![
2496 ("foo".into(), Expr::val(77)),
2497 ("bar".into(), Expr::val("pancakes")),
2498 ])
2499 .unwrap(),
2500 "pancakes".into()
2501 )),
2502 Ok(Value::from(false))
2503 );
2504 assert_eq!(
2506 eval.interpret_inline_policy(&Expr::has_attr(
2507 Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap(),
2508 "2".into()
2509 )),
2510 Ok(Value::from(true))
2511 );
2512 assert_eq!(
2514 eval.interpret_inline_policy(&Expr::has_attr(
2515 Expr::record(vec![
2516 ("ham".into(), Expr::val(17)),
2517 (
2518 "eggs".into(),
2519 Expr::ite(
2520 Expr::has_attr(
2521 Expr::val(EntityUID::with_eid("foo")),
2522 "spaghetti".into()
2523 ),
2524 Expr::val(3),
2525 Expr::val(7)
2526 )
2527 ),
2528 ])
2529 .unwrap(),
2530 "ham".into()
2531 )),
2532 Ok(Value::from(true))
2533 );
2534 assert_matches!(
2536 eval.interpret_inline_policy(&Expr::get_attr(Expr::val(1010122), "hello".into())),
2537 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2538 assert_eq!(expected, nonempty![
2539 Type::Record,
2540 Type::entity_type(
2541 Name::parse_unqualified_name("any_entity_type")
2542 .expect("should be a valid identifier")
2543 ),
2544 ]);
2545 assert_eq!(actual, Type::Long);
2546 assert_eq!(advice, None);
2547 }
2548 );
2549 assert_matches!(
2551 eval.interpret_inline_policy(&Expr::get_attr(Expr::val("hello"), "eggs".into())),
2552 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2553 assert_eq!(expected, nonempty![
2554 Type::Record,
2555 Type::entity_type(
2556 Name::parse_unqualified_name("any_entity_type")
2557 .expect("should be a valid identifier")
2558 ),
2559 ]);
2560 assert_eq!(actual, Type::String);
2561 assert_eq!(advice, None);
2562 }
2563 );
2564 assert_matches!(
2566 eval.interpret_inline_policy(&Expr::has_attr(Expr::val(1010122), "hello".into())),
2567 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2568 assert_eq!(expected, nonempty![
2569 Type::Record,
2570 Type::entity_type(
2571 Name::parse_unqualified_name("any_entity_type")
2572 .expect("should be a valid identifier")
2573 ),
2574 ]);
2575 assert_eq!(actual, Type::Long);
2576 assert_eq!(advice, None);
2577 }
2578 );
2579 assert_matches!(
2581 eval.interpret_inline_policy(&Expr::has_attr(Expr::val("hello"), "eggs".into())),
2582 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2583 assert_eq!(expected, nonempty![
2584 Type::Record,
2585 Type::entity_type(
2586 Name::parse_unqualified_name("any_entity_type")
2587 .expect("should be a valid identifier")
2588 ),
2589 ]);
2590 assert_eq!(actual, Type::String);
2591 assert_eq!(advice, None);
2592 }
2593 );
2594 }
2595
2596 #[test]
2597 fn large_entity_err() {
2598 let expr = Expr::get_attr(
2599 Expr::val(EntityUID::from_str(r#"Foo::"bar""#).unwrap()),
2600 "foo".into(),
2601 );
2602 let attrs = (1..=7)
2603 .map(|id| (format!("{id}").into(), RestrictedExpr::val(true)))
2604 .collect::<HashMap<SmolStr, _>>();
2605 let entity = Entity::new(
2606 r#"Foo::"bar""#.parse().unwrap(),
2607 attrs,
2608 HashSet::new(),
2609 HashSet::new(),
2610 [],
2611 Extensions::none(),
2612 )
2613 .unwrap();
2614 let request = basic_request();
2615 let entities = Entities::from_entities(
2616 [entity],
2617 None::<&NoEntitiesSchema>,
2618 TCComputation::ComputeNow,
2619 Extensions::none(),
2620 )
2621 .unwrap();
2622 let eval = Evaluator::new(request, &entities, Extensions::none());
2623 let result = eval.interpret_inline_policy(&expr).unwrap_err();
2624 let expected_keys = ["1", "2", "3", "4", "5"]
2626 .into_iter()
2627 .map(|x| x.into())
2628 .collect::<Vec<SmolStr>>();
2629 let expected = EvaluationError::entity_attr_does_not_exist(
2630 Arc::new(r#"Foo::"bar""#.parse().unwrap()),
2631 "foo".into(),
2632 expected_keys.iter(),
2633 false,
2634 7,
2635 None,
2636 );
2637 assert_eq!(result, expected);
2638 }
2639
2640 #[test]
2641 fn large_record_err() {
2642 let expr = Expr::get_attr(
2643 Expr::record((1..=7).map(|id| (format!("{id}").into(), Expr::val(true)))).unwrap(),
2644 "foo".into(),
2645 );
2646 let request = basic_request();
2647 let entities = rich_entities();
2648 let eval = Evaluator::new(request, &entities, Extensions::none());
2649 let result = eval.interpret_inline_policy(&expr).unwrap_err();
2650 let first_five = (1..=5)
2651 .map(|id| format!("{id}").into())
2652 .collect::<Vec<SmolStr>>();
2653 let expected =
2654 EvaluationError::record_attr_does_not_exist("foo".into(), first_five.iter(), 7, None);
2655 assert_eq!(result, expected);
2656 }
2657
2658 #[test]
2659 fn interpret_nots() {
2660 let request = basic_request();
2661 let entities = basic_entities();
2662 let eval = Evaluator::new(request, &entities, Extensions::none());
2663 assert_eq!(
2665 eval.interpret_inline_policy(&Expr::not(Expr::val(true))),
2666 Ok(Value::from(false))
2667 );
2668 assert_eq!(
2670 eval.interpret_inline_policy(&Expr::not(Expr::val(false))),
2671 Ok(Value::from(true))
2672 );
2673 assert_matches!(
2675 eval.interpret_inline_policy(&Expr::not(Expr::val(8))),
2676 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2677 assert_eq!(expected, nonempty![Type::Bool]);
2678 assert_eq!(actual, Type::Long);
2679 assert_eq!(advice, None);
2680 }
2681 );
2682 assert_matches!(
2684 eval.interpret_inline_policy(&Expr::not(Expr::var(Var::Action))),
2685 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2686 assert_eq!(expected, nonempty![Type::Bool]);
2687 assert_eq!(actual, Type::Entity {
2688 ty: EntityUID::test_entity_type(),
2689 });
2690 assert_eq!(advice, None);
2691 }
2692 );
2693 assert_eq!(
2695 eval.interpret_inline_policy(&Expr::not(Expr::not(Expr::val(true)))),
2696 Ok(Value::from(true))
2697 );
2698 assert_eq!(
2700 eval.interpret_inline_policy(&Expr::not(Expr::ite(
2701 Expr::val(true),
2702 Expr::val(false),
2703 Expr::val(true)
2704 ))),
2705 Ok(Value::from(true))
2706 );
2707 assert_eq!(
2709 eval.interpret_inline_policy(&Expr::ite(
2710 Expr::not(Expr::val(true)),
2711 Expr::val("hello"),
2712 Expr::val("goodbye")
2713 )),
2714 Ok(Value::from("goodbye"))
2715 );
2716 }
2717
2718 #[test]
2719 fn interpret_negs() {
2720 let request = basic_request();
2721 let entities = basic_entities();
2722 let eval = Evaluator::new(request, &entities, Extensions::none());
2723 assert_eq!(
2725 eval.interpret_inline_policy(&Expr::neg(Expr::val(101))),
2726 Ok(Value::from(-101))
2727 );
2728 assert_eq!(
2730 eval.interpret_inline_policy(&Expr::neg(Expr::val(-101))),
2731 Ok(Value::from(101))
2732 );
2733 assert_eq!(
2735 eval.interpret_inline_policy(&Expr::neg(Expr::val(0))),
2736 Ok(Value::from(0))
2737 );
2738 assert_eq!(
2740 eval.interpret_inline_policy(&Expr::neg(Expr::neg(Expr::val(7)))),
2741 Ok(Value::from(7))
2742 );
2743 assert_eq!(
2745 eval.interpret_inline_policy(&Expr::ite(
2746 Expr::val(true),
2747 Expr::neg(Expr::val(8)),
2748 Expr::neg(Expr::val(1))
2749 )),
2750 Ok(Value::from(-8))
2751 );
2752 assert_eq!(
2754 eval.interpret_inline_policy(&Expr::neg(Expr::val(Integer::MIN))),
2755 Err(IntegerOverflowError::UnaryOp(UnaryOpOverflowError {
2756 op: UnaryOp::Neg,
2757 arg: Value::from(Integer::MIN),
2758 source_loc: None,
2759 })
2760 .into()),
2761 );
2762 assert_matches!(
2764 eval.interpret_inline_policy(&Expr::neg(Expr::val(false))),
2765 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2766 assert_eq!(expected, nonempty![Type::Long]);
2767 assert_eq!(actual, Type::Bool);
2768 assert_eq!(advice, None);
2769 }
2770 );
2771 assert_matches!(
2773 eval.interpret_inline_policy(&Expr::neg(Expr::set([
2774 Expr::val(1),
2775 Expr::val(2),
2776 Expr::val(3)
2777 ]))),
2778 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2779 assert_eq!(expected, nonempty![Type::Long]);
2780 assert_eq!(actual, Type::Set);
2781 assert_eq!(advice, None);
2782 }
2783 );
2784 }
2785
2786 #[test]
2787 fn interpret_eqs() {
2788 let request = basic_request();
2789 let entities = basic_entities();
2790 let eval = Evaluator::new(request, &entities, Extensions::none());
2791 assert_eq!(
2793 eval.interpret_inline_policy(&Expr::is_eq(Expr::val(33), Expr::val(33))),
2794 Ok(Value::from(true))
2795 );
2796 assert_eq!(
2798 eval.interpret_inline_policy(&Expr::is_eq(Expr::val(33), Expr::val(-12))),
2799 Ok(Value::from(false))
2800 );
2801 assert_eq!(
2803 eval.interpret_inline_policy(&Expr::ite(
2804 Expr::is_eq(Expr::val("foo"), Expr::val("foo")),
2805 Expr::val(12),
2806 Expr::val(97),
2807 )),
2808 Ok(Value::from(12))
2809 );
2810 assert_eq!(
2812 eval.interpret_inline_policy(&Expr::ite(
2813 Expr::is_eq(
2814 Expr::set(vec![Expr::val(1), Expr::val(-33), Expr::val(707)]),
2815 Expr::set(vec![Expr::val(1), Expr::val(-33)])
2816 ),
2817 Expr::val(12),
2818 Expr::val(97),
2819 )),
2820 Ok(Value::from(97))
2821 );
2822 assert_eq!(
2824 eval.interpret_inline_policy(&Expr::is_eq(
2825 Expr::greater(Expr::val(2), Expr::val(0)),
2826 Expr::greater(Expr::val(0), Expr::val(-2))
2827 )),
2828 Ok(Value::from(true))
2829 );
2830 assert_eq!(
2832 eval.interpret_inline_policy(&Expr::is_eq(
2833 Expr::add(Expr::val(12), Expr::val(33)),
2834 Expr::sub(Expr::val(50), Expr::val(5)),
2835 )),
2836 Ok(Value::from(true))
2837 );
2838 assert_eq!(
2840 eval.interpret_inline_policy(&Expr::is_eq(
2841 Expr::set(vec![Expr::val(1), Expr::val(2), Expr::val(40)]),
2842 Expr::set(vec![Expr::val(1), Expr::val(2), Expr::val(40)])
2843 )),
2844 Ok(Value::from(true))
2845 );
2846 assert_eq!(
2848 eval.interpret_inline_policy(&Expr::is_eq(
2849 Expr::set(vec![Expr::val(1), Expr::val(2), Expr::val(40)]),
2850 Expr::set(vec![Expr::val(1), Expr::val(40), Expr::val(2)])
2851 )),
2852 Ok(Value::from(true))
2853 );
2854 assert_eq!(
2856 eval.interpret_inline_policy(&Expr::is_eq(
2857 Expr::set(vec![Expr::val(1), Expr::val(-2), Expr::val(40)]),
2858 Expr::set(vec![Expr::val(1), Expr::val(40)])
2859 )),
2860 Ok(Value::from(false))
2861 );
2862 assert_eq!(
2864 eval.interpret_inline_policy(&Expr::is_eq(
2865 Expr::set(vec![
2866 Expr::val(1),
2867 Expr::val(1),
2868 Expr::val(1),
2869 Expr::val(2),
2870 Expr::val(40)
2871 ]),
2872 Expr::set(vec![Expr::val(40), Expr::val(1), Expr::val(2)])
2873 )),
2874 Ok(Value::from(true))
2875 );
2876 assert_eq!(
2878 eval.interpret_inline_policy(&Expr::is_eq(
2879 Expr::set(vec![
2880 Expr::val(1),
2881 Expr::val(1),
2882 Expr::val(2),
2883 Expr::val(1),
2884 Expr::val(40),
2885 Expr::val(2),
2886 Expr::val(1),
2887 Expr::val(2),
2888 Expr::val(40),
2889 Expr::val(1)
2890 ]),
2891 Expr::set(vec![
2892 Expr::val(1),
2893 Expr::val(40),
2894 Expr::val(1),
2895 Expr::val(2)
2896 ])
2897 )),
2898 Ok(Value::from(true))
2899 );
2900 assert_eq!(
2902 eval.interpret_inline_policy(&Expr::is_eq(
2903 Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2904 Expr::record(vec![
2905 ("os_name".into(), Expr::val("Windows")),
2906 ("manufacturer".into(), Expr::val("ACME Corp")),
2907 ])
2908 .unwrap()
2909 )),
2910 Ok(Value::from(true))
2911 );
2912 assert_eq!(
2914 eval.interpret_inline_policy(&Expr::is_eq(
2915 Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2916 Expr::record(vec![("os_name".into(), Expr::val("Windows"))]).unwrap()
2917 )),
2918 Ok(Value::from(false))
2919 );
2920 assert_eq!(
2922 eval.interpret_inline_policy(&Expr::is_eq(
2923 Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2924 Expr::record(vec![
2925 ("os_name".into(), Expr::val("Windows")),
2926 ("manufacturer".into(), Expr::val("ACME Corp")),
2927 ("extrafield".into(), Expr::val(true)),
2928 ])
2929 .unwrap()
2930 )),
2931 Ok(Value::from(false))
2932 );
2933 assert_eq!(
2935 eval.interpret_inline_policy(&Expr::is_eq(
2936 Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2937 Expr::record(vec![
2938 ("os_name".into(), Expr::val("Windows")),
2939 ("manufacturer".into(), Expr::val("ACME Corp")),
2940 ])
2941 .unwrap()
2942 )),
2943 Ok(Value::from(true))
2944 );
2945 assert_eq!(
2947 eval.interpret_inline_policy(&Expr::is_eq(
2948 Expr::val(EntityUID::with_eid("foo")),
2949 Expr::val(EntityUID::with_eid("foo")),
2950 )),
2951 Ok(Value::from(true))
2952 );
2953 assert_eq!(
2955 eval.interpret_inline_policy(&Expr::is_eq(
2956 Expr::val(EntityUID::with_eid("doesnotexist")),
2957 Expr::val(EntityUID::with_eid("doesnotexist")),
2958 )),
2959 Ok(Value::from(true))
2960 );
2961 assert_eq!(
2963 eval.interpret_inline_policy(&Expr::is_eq(
2964 Expr::val(EntityUID::with_eid("foo")),
2965 Expr::val(EntityUID::with_eid("bar")),
2966 )),
2967 Ok(Value::from(false))
2968 );
2969 assert_eq!(
2971 eval.interpret_inline_policy(&Expr::is_eq(
2972 Expr::val(
2973 EntityUID::with_eid_and_type("type1", "foo")
2974 .expect("should be a valid identifier")
2975 ),
2976 Expr::val(
2977 EntityUID::with_eid_and_type("type2", "bar")
2978 .expect("should be a valid identifier")
2979 ),
2980 )),
2981 Ok(Value::from(false))
2982 );
2983 assert_eq!(
2986 eval.interpret_inline_policy(&Expr::is_eq(
2987 Expr::val(
2988 EntityUID::with_eid_and_type("type1", "foo")
2989 .expect("should be a valid identifier")
2990 ),
2991 Expr::val(
2992 EntityUID::with_eid_and_type("type2", "foo")
2993 .expect("should be a valid identifier")
2994 ),
2995 )),
2996 Ok(Value::from(false))
2997 );
2998 assert_eq!(
3000 eval.interpret_inline_policy(&Expr::is_eq(
3001 Expr::val(EntityUID::with_eid("foo")),
3002 Expr::val(EntityUID::with_eid("doesnotexist")),
3003 )),
3004 Ok(Value::from(false))
3005 );
3006 assert_eq!(
3008 eval.interpret_inline_policy(&Expr::is_eq(
3009 Expr::val("foo"),
3010 Expr::val(EntityUID::with_eid("foo"))
3011 )),
3012 Ok(Value::from(false))
3013 );
3014 }
3015
3016 #[test]
3017 fn interpret_compares() {
3018 let request = basic_request();
3019 let entities = basic_entities();
3020 let extensions = Extensions::all_available();
3021 let eval = Evaluator::new(request, &entities, extensions);
3022 let expected_types = valid_comparison_op_types(extensions);
3023 let assert_type_error = |expr, actual_type| {
3024 assert_matches!(
3025 eval.interpret_inline_policy(&expr),
3026 Err(EvaluationError::TypeError(TypeError { expected, actual, .. })) => {
3027 assert_eq!(expected, expected_types.clone());
3028 assert_eq!(actual, actual_type);
3029 }
3030 );
3031 };
3032 assert_eq!(
3034 eval.interpret_inline_policy(&Expr::less(Expr::val(3), Expr::val(303))),
3035 Ok(Value::from(true))
3036 );
3037 assert_eq!(
3039 eval.interpret_inline_policy(&Expr::less(Expr::val(3), Expr::val(-303))),
3040 Ok(Value::from(false))
3041 );
3042 assert_eq!(
3044 eval.interpret_inline_policy(&Expr::less(Expr::val(-303), Expr::val(-1))),
3045 Ok(Value::from(true))
3046 );
3047 assert_eq!(
3049 eval.interpret_inline_policy(&Expr::less(Expr::val(3), Expr::val(3))),
3050 Ok(Value::from(false))
3051 );
3052 assert_eq!(
3054 eval.interpret_inline_policy(&Expr::lesseq(Expr::val(-33), Expr::val(0))),
3055 Ok(Value::from(true))
3056 );
3057 assert_eq!(
3059 eval.interpret_inline_policy(&Expr::lesseq(Expr::val(3), Expr::val(3))),
3060 Ok(Value::from(true))
3061 );
3062 assert_eq!(
3064 eval.interpret_inline_policy(&Expr::greater(Expr::val(7), Expr::val(3))),
3065 Ok(Value::from(true))
3066 );
3067 assert_eq!(
3069 eval.interpret_inline_policy(&Expr::greater(Expr::val(7), Expr::val(-3))),
3070 Ok(Value::from(true))
3071 );
3072 assert_eq!(
3074 eval.interpret_inline_policy(&Expr::greater(Expr::val(7), Expr::val(7))),
3075 Ok(Value::from(false))
3076 );
3077 assert_eq!(
3079 eval.interpret_inline_policy(&Expr::greatereq(Expr::val(0), Expr::val(-7))),
3080 Ok(Value::from(true))
3081 );
3082 assert_eq!(
3084 eval.interpret_inline_policy(&Expr::greatereq(Expr::val(-1), Expr::val(7))),
3085 Ok(Value::from(false))
3086 );
3087 assert_eq!(
3089 eval.interpret_inline_policy(&Expr::greatereq(Expr::val(7), Expr::val(7))),
3090 Ok(Value::from(true))
3091 );
3092 assert_type_error(Expr::less(Expr::val(false), Expr::val(true)), Type::Bool);
3094
3095 assert_type_error(Expr::less(Expr::val(false), Expr::val(false)), Type::Bool);
3097
3098 assert_type_error(Expr::lesseq(Expr::val(true), Expr::val(false)), Type::Bool);
3100
3101 assert_type_error(Expr::lesseq(Expr::val(false), Expr::val(false)), Type::Bool);
3103
3104 assert_type_error(Expr::greater(Expr::val(false), Expr::val(true)), Type::Bool);
3106
3107 assert_type_error(Expr::greater(Expr::val(true), Expr::val(true)), Type::Bool);
3109
3110 assert_type_error(
3112 Expr::greatereq(Expr::val(true), Expr::val(false)),
3113 Type::Bool,
3114 );
3115
3116 assert_type_error(
3118 Expr::greatereq(Expr::val(true), Expr::val(true)),
3119 Type::Bool,
3120 );
3121
3122 assert_type_error(Expr::less(Expr::val("bc"), Expr::val("zzz")), Type::String);
3124 assert_type_error(
3126 Expr::less(Expr::val("banana"), Expr::val("zzz")),
3127 Type::String,
3128 );
3129 assert_type_error(Expr::less(Expr::val(""), Expr::val("zzz")), Type::String);
3131 assert_type_error(Expr::less(Expr::val("a"), Expr::val("1")), Type::String);
3133 assert_type_error(Expr::less(Expr::val("a"), Expr::val("A")), Type::String);
3135 assert_type_error(Expr::less(Expr::val("A"), Expr::val("A")), Type::String);
3137 assert_type_error(
3139 Expr::less(Expr::val("zebra"), Expr::val("zebras")),
3140 Type::String,
3141 );
3142 assert_type_error(
3144 Expr::lesseq(Expr::val("zebra"), Expr::val("zebras")),
3145 Type::String,
3146 );
3147 assert_type_error(
3149 Expr::lesseq(Expr::val("zebras"), Expr::val("zebras")),
3150 Type::String,
3151 );
3152 assert_type_error(
3154 Expr::lesseq(Expr::val("zebras"), Expr::val("Zebras")),
3155 Type::String,
3156 );
3157 assert_type_error(
3159 Expr::greater(Expr::val("123"), Expr::val("78")),
3160 Type::String,
3161 );
3162 assert_type_error(
3164 Expr::greatereq(Expr::val(" zebras"), Expr::val("zebras")),
3165 Type::String,
3166 );
3167 assert_type_error(Expr::greatereq(Expr::val(""), Expr::val("")), Type::String);
3169 assert_type_error(
3171 Expr::greatereq(Expr::val(""), Expr::val("_hi")),
3172 Type::String,
3173 );
3174 assert_type_error(
3176 Expr::greatereq(Expr::val("🦀"), Expr::val("_hi")),
3177 Type::String,
3178 );
3179 assert_matches!(
3181 eval.interpret_inline_policy(&Expr::less(Expr::val(2), Expr::val("4"))),
3182 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3183 assert_eq!(expected, nonempty![Type::Long]);
3184 assert_eq!(actual, Type::String);
3185 assert_eq!(advice, None);
3186 }
3187 );
3188 assert_matches!(
3190 eval.interpret_inline_policy(&Expr::less(Expr::val("4"), Expr::val(2))),
3191 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3192 assert_eq!(expected, nonempty![Type::Long]);
3193 assert_eq!(actual, Type::String);
3194 assert_eq!(advice, None);
3195 }
3196 );
3197 assert_matches!(
3199 eval.interpret_inline_policy(&Expr::less(Expr::val(false), Expr::val(1))),
3200 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3201 assert_eq!(expected, nonempty![Type::Long]);
3202 assert_eq!(actual, Type::Bool);
3203 assert_eq!(advice, None);
3204 }
3205 );
3206 assert_matches!(
3208 eval.interpret_inline_policy(&Expr::less(Expr::val(1), Expr::val(false))),
3209 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3210 assert_eq!(expected, nonempty![Type::Long]);
3211 assert_eq!(actual, Type::Bool);
3212 assert_eq!(advice, None);
3213 }
3214 );
3215 assert_type_error(
3217 Expr::less(
3218 Expr::set(vec![Expr::val(1), Expr::val(2)]),
3219 Expr::set(vec![Expr::val(47), Expr::val(0)]),
3220 ),
3221 Type::Set,
3222 );
3223 }
3224
3225 #[test]
3226 fn interpret_datetime_extension_compares() {
3227 let request = basic_request();
3228 let entities = basic_entities();
3229 let extensions = Extensions::all_available();
3230 let eval = Evaluator::new(request, &entities, extensions);
3231 let datetime_constructor: Name = "datetime".parse().unwrap();
3232 let duration_constructor: Name = "duration".parse().unwrap();
3233 assert_matches!(eval.interpret_inline_policy(
3234 &Expr::less(
3235 Expr::call_extension_fn(
3236 datetime_constructor.clone(),
3237 vec![Value::from("2024-01-01").into()]),
3238 Expr::call_extension_fn(
3239 datetime_constructor.clone(),
3240 vec![Value::from("2024-01-23").into()]))),
3241 Ok(v) if v == Value::from(true));
3242 assert_matches!(eval.interpret_inline_policy(
3243 &Expr::lesseq(
3244 Expr::call_extension_fn(
3245 datetime_constructor.clone(),
3246 vec![Value::from("2024-01-01").into()]),
3247 Expr::call_extension_fn(
3248 datetime_constructor.clone(),
3249 vec![Value::from("2024-01-23").into()]))),
3250 Ok(v) if v == Value::from(true));
3251 assert_matches!(eval.interpret_inline_policy(
3252 &Expr::less(
3253 Expr::call_extension_fn(
3254 datetime_constructor.clone(),
3255 vec![Value::from("2024-01-01T01:02:03Z").into()]),
3256 Expr::call_extension_fn(
3257 datetime_constructor.clone(),
3258 vec![Value::from("2023-01-23").into()]))),
3259 Ok(v) if v == Value::from(false));
3260 assert_matches!(eval.interpret_inline_policy(
3261 &Expr::lesseq(
3262 Expr::call_extension_fn(
3263 datetime_constructor.clone(),
3264 vec![Value::from("2024-01-01T01:02:03Z").into()]),
3265 Expr::call_extension_fn(
3266 datetime_constructor.clone(),
3267 vec![Value::from("2023-01-23").into()]))),
3268 Ok(v) if v == Value::from(false));
3269 assert_matches!(eval.interpret_inline_policy(
3270 &Expr::less(
3271 Expr::call_extension_fn(
3272 duration_constructor.clone(),
3273 vec![Value::from("5s").into()]),
3274 Expr::call_extension_fn(
3275 duration_constructor.clone(),
3276 vec![Value::from("2m").into()]))),
3277 Ok(v) if v == Value::from(true));
3278 assert_matches!(eval.interpret_inline_policy(
3279 &Expr::lesseq(
3280 Expr::call_extension_fn(
3281 duration_constructor.clone(),
3282 vec![Value::from("1h").into()]),
3283 Expr::call_extension_fn(
3284 duration_constructor.clone(),
3285 vec![Value::from("2h").into()]))),
3286 Ok(v) if v == Value::from(true));
3287 assert_matches!(eval.interpret_inline_policy(
3288 &Expr::less(
3289 Expr::call_extension_fn(
3290 duration_constructor.clone(),
3291 vec![Value::from("3h2m").into()]),
3292 Expr::call_extension_fn(
3293 duration_constructor.clone(),
3294 vec![Value::from("2h").into()]))),
3295 Ok(v) if v == Value::from(false));
3296 assert_matches!(eval.interpret_inline_policy(
3297 &Expr::lesseq(
3298 Expr::call_extension_fn(
3299 duration_constructor.clone(),
3300 vec![Value::from("3h2m").into()]),
3301 Expr::call_extension_fn(
3302 duration_constructor.clone(),
3303 vec![Value::from("2h").into()]))),
3304 Ok(v) if v == Value::from(false));
3305
3306 assert_matches!(eval.interpret_inline_policy(
3308 &Expr::noteq(
3309 Expr::call_extension_fn(
3310 datetime_constructor.clone(),
3311 vec![Value::from("2024-11-07").into()]),
3312 Expr::call_extension_fn(
3313 datetime_constructor.clone(),
3314 vec![Value::from("2024-11-07T14:00:00Z").into()]))),
3315 Ok(v) if v == Value::from(true));
3316 assert_matches!(eval.interpret_inline_policy(
3317 &Expr::noteq(
3318 Expr::call_extension_fn(
3319 datetime_constructor.clone(),
3320 vec![Value::from("2024-11-07T14:00:00.123Z").into()]),
3321 Expr::call_extension_fn(
3322 datetime_constructor.clone(),
3323 vec![Value::from("2024-11-07T14:00:00Z").into()]))),
3324 Ok(v) if v == Value::from(true));
3325 assert_matches!(eval.interpret_inline_policy(
3326 &Expr::noteq(
3327 Expr::call_extension_fn(
3328 datetime_constructor.clone(),
3329 vec![Value::from("2024-11-07T14:00:00Z").into()]),
3330 Expr::call_extension_fn(
3331 datetime_constructor.clone(),
3332 vec![Value::from("2024-11-07T17:00:00Z").into()]))),
3333 Ok(v) if v == Value::from(true));
3334
3335 assert_matches!(eval.interpret_inline_policy(
3338 &Expr::noteq(
3339 Expr::call_extension_fn(
3340 datetime_constructor.clone(),
3341 vec![Value::from("2024-11-07T14:00:00+0200").into()]),
3342 Expr::call_extension_fn(
3343 datetime_constructor.clone(),
3344 vec![Value::from("2024-11-07T11:00:00-0100").into()]))),
3345 Ok(v) if v == Value::from(false));
3346 assert_matches!(eval.interpret_inline_policy(
3348 &Expr::noteq(
3349 Expr::call_extension_fn(
3350 datetime_constructor.clone(),
3351 vec![Value::from("2024-11-08T02:00:00+0200").into()]),
3352 Expr::call_extension_fn(
3353 datetime_constructor.clone(),
3354 vec![Value::from("2024-11-07T23:00:00-0100").into()]))),
3355 Ok(v) if v == Value::from(false));
3356
3357 assert_matches!(eval.interpret_inline_policy(
3359 &Expr::less(
3360 Expr::call_extension_fn(
3361 datetime_constructor.clone(),
3362 vec![Value::from("2024-02-28").into()]),
3363 Expr::call_extension_fn(
3364 datetime_constructor.clone(),
3365 vec![Value::from("2024-02-29").into()]))),
3366 Ok(v) if v == Value::from(true));
3367 assert_matches!(eval.interpret_inline_policy(
3368 &Expr::less(
3369 Expr::call_extension_fn(
3370 datetime_constructor.clone(),
3371 vec![Value::from("2024-02-29").into()]),
3372 Expr::call_extension_fn(
3373 datetime_constructor.clone(),
3374 vec![Value::from("2024-03-01").into()]))),
3375 Ok(v) if v == Value::from(true));
3376
3377 assert_matches!(eval.interpret_inline_policy(
3379 &Expr::lesseq(
3380 Value::from(1).into(),
3381 Expr::call_extension_fn(
3382 duration_constructor.clone(),
3383 vec![Value::from("2h").into()]))),
3384 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3385 assert_eq!(expected, nonempty![Type::Long]);
3386 assert_eq!(actual, Type::Extension { name: duration_constructor.clone() });
3387 assert_eq!(advice, None);
3388 });
3389
3390 assert_matches!(eval.interpret_inline_policy(
3391 &Expr::lesseq(
3392 Expr::call_extension_fn(
3393 duration_constructor.clone(),
3394 vec![Value::from("2h").into()]),
3395 Value::from(1).into())),
3396 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3397 assert_eq!(expected, nonempty![Type::Long]);
3398 assert_eq!(actual, Type::Extension { name: duration_constructor.clone() });
3399 assert_eq!(advice, None);
3400 });
3401
3402 assert_matches!(eval.interpret_inline_policy(
3403 &Expr::lesseq(
3404 Expr::call_extension_fn(
3405 duration_constructor.clone(),
3406 vec![Value::from("2h").into()]),
3407 Expr::call_extension_fn(
3408 "decimal".parse().unwrap(),
3409 vec![Value::from("2.0").into()]))),
3410 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3411 assert_eq!(expected, nonempty![Type::Extension { name: duration_constructor.clone() }]);
3412 assert_eq!(actual, Type::Extension { name: "decimal".parse().unwrap() });
3413 assert_eq!(advice, None);
3414 });
3415
3416 assert_matches!(eval.interpret_inline_policy(
3417 &Expr::lesseq(
3418 Expr::call_extension_fn(
3419 "decimal".parse().unwrap(),
3420 vec![Value::from("2.0").into()]),
3421 Expr::call_extension_fn(
3422 duration_constructor.clone(),
3423 vec![Value::from("2h").into()]))),
3424 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3425 assert_eq!(expected, nonempty![Type::Extension { name: duration_constructor.clone() }]);
3426 assert_eq!(actual, Type::Extension { name: "decimal".parse().unwrap() });
3427 assert_eq!(advice, None);
3428 });
3429
3430 assert_matches!(eval.interpret_inline_policy(
3432 &Expr::lesseq(
3433 Expr::call_extension_fn(
3434 datetime_constructor.clone(),
3435 vec![Value::from("2023-01-23").into()]),
3436 Expr::call_extension_fn(
3437 duration_constructor.clone(),
3438 vec![Value::from("2h").into()]))),
3439 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3440 assert_eq!(expected, nonempty![Type::Extension { name: datetime_constructor }]);
3441 assert_eq!(actual, Type::Extension { name: duration_constructor });
3442 assert_eq!(advice, None);
3443 });
3444
3445 assert_matches!(eval.interpret_inline_policy(
3447 &Expr::lesseq(
3448 Expr::call_extension_fn(
3449 "decimal".parse().unwrap(),
3450 vec![Value::from("2.0").into()]),
3451 Expr::call_extension_fn(
3452 "decimal".parse().unwrap(),
3453 vec![Value::from("3.0").into()]))),
3454 Err(EvaluationError::TypeError(TypeError { expected, actual, .. })) => {
3455 assert_eq!(expected, valid_comparison_op_types(extensions));
3456 assert_eq!(actual, Type::Extension { name: "decimal".parse().unwrap() });
3457 });
3458 }
3459
3460 #[test]
3461 fn interpret_comparison_err_order() {
3462 let request = basic_request();
3466 let entities = basic_entities();
3467 let eval = Evaluator::new(request, &entities, Extensions::none());
3468
3469 assert_matches!(
3470 eval.interpret_inline_policy(&Expr::greatereq(
3471 Expr::add(Expr::val("a"), Expr::val("b")),
3472 Expr::add(Expr::val(false), Expr::val(true))
3473 )),
3474 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3475 assert_eq!(expected, nonempty![Type::Long]);
3476 assert_eq!(actual, Type::String);
3477 assert_eq!(advice, None);
3478 }
3479 );
3480
3481 assert_matches!(
3482 eval.interpret_inline_policy(&Expr::greater(
3483 Expr::add(Expr::val("a"), Expr::val("b")),
3484 Expr::add(Expr::val(false), Expr::val(true))
3485 )),
3486 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3487 assert_eq!(expected, nonempty![Type::Long]);
3488 assert_eq!(actual, Type::String);
3489 assert_eq!(advice, None);
3490 }
3491 );
3492
3493 assert_matches!(
3494 eval.interpret_inline_policy(&Expr::lesseq(
3495 Expr::add(Expr::val("a"), Expr::val("b")),
3496 Expr::add(Expr::val(false), Expr::val(true))
3497 )),
3498 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3499 assert_eq!(expected, nonempty![Type::Long]);
3500 assert_eq!(actual, Type::String);
3501 assert_eq!(advice, None);
3502 }
3503 );
3504
3505 assert_matches!(
3506 eval.interpret_inline_policy(&Expr::less(
3507 Expr::add(Expr::val("a"), Expr::val("b")),
3508 Expr::add(Expr::val(false), Expr::val(true))
3509 )),
3510 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3511 assert_eq!(expected, nonempty![Type::Long]);
3512 assert_eq!(actual, Type::String);
3513 assert_eq!(advice, None);
3514 }
3515 );
3516 }
3517
3518 #[test]
3519 fn interpret_arithmetic() {
3520 let request = basic_request();
3521 let entities = basic_entities();
3522 let eval = Evaluator::new(request, &entities, Extensions::none());
3523 assert_eq!(
3525 eval.interpret_inline_policy(&Expr::add(Expr::val(11), Expr::val(22))),
3526 Ok(Value::from(33))
3527 );
3528 assert_eq!(
3530 eval.interpret_inline_policy(&Expr::add(Expr::val(11), Expr::val(0))),
3531 Ok(Value::from(11))
3532 );
3533 assert_eq!(
3535 eval.interpret_inline_policy(&Expr::add(Expr::val(-1), Expr::val(1))),
3536 Ok(Value::from(0))
3537 );
3538 assert_eq!(
3540 eval.interpret_inline_policy(&Expr::add(Expr::val(Integer::MAX), Expr::val(1))),
3541 Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
3542 op: BinaryOp::Add,
3543 arg1: Value::from(Integer::MAX),
3544 arg2: Value::from(1),
3545 source_loc: None,
3546 })
3547 .into())
3548 );
3549 assert_matches!(
3551 eval.interpret_inline_policy(&Expr::add(Expr::val(7), Expr::val("3"))),
3552 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3553 assert_eq!(expected, nonempty![Type::Long]);
3554 assert_eq!(actual, Type::String);
3555 assert_eq!(advice, None);
3556 }
3557 );
3558 assert_eq!(
3560 eval.interpret_inline_policy(&Expr::sub(Expr::val(44), Expr::val(31))),
3561 Ok(Value::from(13))
3562 );
3563 assert_eq!(
3565 eval.interpret_inline_policy(&Expr::sub(Expr::val(5), Expr::val(-3))),
3566 Ok(Value::from(8))
3567 );
3568 assert_eq!(
3570 eval.interpret_inline_policy(&Expr::sub(Expr::val(Integer::MIN + 2), Expr::val(3))),
3571 Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
3572 op: BinaryOp::Sub,
3573 arg1: Value::from(Integer::MIN + 2),
3574 arg2: Value::from(3),
3575 source_loc: None,
3576 })
3577 .into())
3578 );
3579 assert_matches!(
3581 eval.interpret_inline_policy(&Expr::sub(Expr::val("ham"), Expr::val("ha"))),
3582 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3583 assert_eq!(expected, nonempty![Type::Long]);
3584 assert_eq!(actual, Type::String);
3585 assert_eq!(advice, None);
3586 }
3587 );
3588 assert_eq!(
3590 eval.interpret_inline_policy(&Expr::mul(Expr::val(5), Expr::val(-3))),
3591 Ok(Value::from(-15))
3592 );
3593 assert_eq!(
3595 eval.interpret_inline_policy(&Expr::mul(Expr::val(5), Expr::val(0))),
3596 Ok(Value::from(0))
3597 );
3598 assert_matches!(
3600 eval.interpret_inline_policy(&Expr::mul(Expr::val("5"), Expr::val(0))),
3601 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3602 assert_eq!(expected, nonempty![Type::Long]);
3603 assert_eq!(actual, Type::String);
3604 assert_eq!(advice, None);
3605 }
3606 );
3607 assert_eq!(
3609 eval.interpret_inline_policy(&Expr::mul(Expr::val(Integer::MAX - 1), Expr::val(3))),
3610 Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
3611 op: BinaryOp::Mul,
3612 arg1: Value::from(Integer::MAX - 1),
3613 arg2: Value::from(3),
3614 source_loc: None,
3615 })
3616 .into())
3617 );
3618 }
3619
3620 #[test]
3621 fn interpret_set_and_map_membership() {
3622 let request = basic_request();
3623 let entities = rich_entities();
3624 let eval = Evaluator::new(request, &entities, Extensions::none());
3625
3626 assert_eq!(
3628 eval.interpret_inline_policy(&Expr::contains(
3629 Expr::set(vec![Expr::val(2), Expr::val(3), Expr::val(4)]),
3630 Expr::val(2)
3631 )),
3632 Ok(Value::from(true))
3633 );
3634 assert_eq!(
3636 eval.interpret_inline_policy(&Expr::contains(
3637 Expr::set(vec![Expr::val(34), Expr::val(2), Expr::val(-7)]),
3638 Expr::val(2)
3639 )),
3640 Ok(Value::from(true))
3641 );
3642 assert_eq!(
3644 eval.interpret_inline_policy(&Expr::contains(
3645 Expr::set(vec![Expr::val(34), Expr::val(2), Expr::val(-7)]),
3646 Expr::val(3)
3647 )),
3648 Ok(Value::from(false))
3649 );
3650 assert_eq!(
3652 eval.interpret_inline_policy(&Expr::contains(Expr::set(vec![]), Expr::val(7))),
3653 Ok(Value::from(false))
3654 );
3655 assert_eq!(
3657 eval.interpret_inline_policy(&Expr::contains(
3658 Expr::set(vec![
3659 Expr::val("some"),
3660 Expr::val("useful"),
3661 Expr::val("tags")
3662 ]),
3663 Expr::val("foo")
3664 )),
3665 Ok(Value::from(false))
3666 );
3667 assert_eq!(
3669 eval.interpret_inline_policy(&Expr::contains(
3670 Expr::set(vec![
3671 Expr::val("some"),
3672 Expr::val("useful"),
3673 Expr::val("tags")
3674 ]),
3675 Expr::val("useful")
3676 )),
3677 Ok(Value::from(true))
3678 );
3679 assert_eq!(
3681 eval.interpret_inline_policy(&Expr::contains(
3682 Expr::set(vec![
3683 Expr::val(EntityUID::with_eid("child")),
3684 Expr::val(EntityUID::with_eid("sibling"))
3685 ]),
3686 Expr::val(EntityUID::with_eid("child"))
3687 )),
3688 Ok(Value::from(true))
3689 );
3690 assert_eq!(
3692 eval.interpret_inline_policy(&Expr::contains(
3693 Expr::set(vec![
3694 Expr::val(EntityUID::with_eid("parent")),
3695 Expr::val(EntityUID::with_eid("sibling"))
3696 ]),
3697 Expr::val(EntityUID::with_eid("child"))
3698 )),
3699 Ok(Value::from(false))
3700 );
3701 assert_eq!(
3703 eval.interpret_inline_policy(&Expr::contains(
3704 Expr::set(vec![Expr::val("foo"), Expr::val("bar")]),
3705 Expr::val(3)
3706 )),
3707 Ok(Value::from(false))
3708 );
3709 assert_eq!(
3711 eval.interpret_inline_policy(&Expr::contains(
3712 Expr::set(vec![Expr::val("foo"), Expr::val("bar")]),
3713 Expr::set(vec![Expr::val(3)])
3714 )),
3715 Ok(Value::from(false))
3716 );
3717 assert_eq!(
3719 eval.interpret_inline_policy(&Expr::contains(
3720 Expr::set(vec![
3721 Expr::set(vec![Expr::val(7)]),
3722 Expr::val("eggs"),
3723 Expr::set(vec![Expr::val(3)])
3724 ]),
3725 Expr::set(vec![Expr::val(3)])
3726 )),
3727 Ok(Value::from(true))
3728 );
3729
3730 assert_eq!(
3732 eval.interpret_inline_policy(&Expr::contains(
3733 Expr::set(vec![
3734 Expr::val("2"),
3735 Expr::val(20),
3736 Expr::val(true),
3737 Expr::val(EntityUID::with_eid("foo")),
3738 ]),
3739 Expr::val(2)
3740 )),
3741 Ok(Value::from(false))
3742 );
3743 assert_eq!(
3745 eval.interpret_inline_policy(&Expr::contains(
3746 Expr::set(vec![
3747 Expr::val("ham"),
3748 Expr::get_attr(
3749 Expr::get_attr(
3750 Expr::val(EntityUID::with_eid("entity_with_attrs")),
3751 "address".into()
3752 ),
3753 "town".into()
3754 ),
3755 Expr::val(-1),
3756 ]),
3757 Expr::val("barmstadt")
3758 )),
3759 Ok(Value::from(true))
3760 );
3761 assert_matches!(
3763 eval.interpret_inline_policy(&Expr::contains(Expr::val(3), Expr::val(7))),
3764 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3765 assert_eq!(expected, nonempty![Type::Set]);
3766 assert_eq!(actual, Type::Long);
3767 assert_eq!(advice, None);
3768 }
3769 );
3770 assert_matches!(
3772 eval.interpret_inline_policy(&Expr::contains(
3773 Expr::record(vec![("ham".into(), Expr::val("eggs"))]).unwrap(),
3774 Expr::val("ham")
3775 )),
3776 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3777 assert_eq!(expected, nonempty![Type::Set]);
3778 assert_eq!(actual, Type::Record);
3779 assert_eq!(advice, None);
3780 }
3781 );
3782 assert_matches!(
3784 eval.interpret_inline_policy(&Expr::contains(
3785 Expr::val(3),
3786 Expr::set(vec![Expr::val(1), Expr::val(3), Expr::val(7)])
3787 )),
3788 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3789 assert_eq!(expected, nonempty![Type::Set]);
3790 assert_eq!(actual, Type::Long);
3791 assert_eq!(advice, None);
3792 }
3793 );
3794 }
3795
3796 #[test]
3797 fn interpret_hierarchy_membership() {
3798 let request = basic_request();
3799 let entities = rich_entities();
3800 let eval = Evaluator::new(request, &entities, Extensions::none());
3801 assert_eq!(
3803 eval.interpret_inline_policy(&Expr::is_in(
3804 Expr::val(EntityUID::with_eid("child")),
3805 Expr::val(EntityUID::with_eid("unrelated"))
3806 )),
3807 Ok(Value::from(false))
3808 );
3809 assert_eq!(
3811 eval.interpret_inline_policy(&Expr::is_in(
3812 Expr::val(EntityUID::with_eid("child")),
3813 Expr::val(EntityUID::with_eid("parent"))
3814 )),
3815 Ok(Value::from(true))
3816 );
3817 assert_eq!(
3819 eval.interpret_inline_policy(&Expr::is_in(
3820 Expr::val(
3821 EntityUID::with_eid_and_type("other_type", "other_child")
3822 .expect("should be a valid identifier")
3823 ),
3824 Expr::val(EntityUID::with_eid("parent"))
3825 )),
3826 Ok(Value::from(true))
3827 );
3828 assert_eq!(
3830 eval.interpret_inline_policy(&Expr::is_in(
3831 Expr::val(
3832 EntityUID::with_eid_and_type("other_type", "other_child")
3833 .expect("should be a valid identifier")
3834 ),
3835 Expr::val(EntityUID::with_eid("unrelated"))
3836 )),
3837 Ok(Value::from(false))
3838 );
3839 assert_eq!(
3841 eval.interpret_inline_policy(&Expr::is_in(
3842 Expr::val(EntityUID::with_eid("child")),
3843 Expr::val(EntityUID::with_eid("sibling"))
3844 )),
3845 Ok(Value::from(false))
3846 );
3847 assert_eq!(
3849 eval.interpret_inline_policy(&Expr::is_in(
3850 Expr::val(EntityUID::with_eid("parent")),
3851 Expr::val(EntityUID::with_eid("parent"))
3852 )),
3853 Ok(Value::from(true))
3854 );
3855 assert_eq!(
3857 eval.interpret_inline_policy(&Expr::is_in(
3858 Expr::val(EntityUID::with_eid("doesnotexist")),
3859 Expr::val(EntityUID::with_eid("doesnotexist")),
3860 )),
3861 Ok(Value::from(true))
3862 );
3863 assert_eq!(
3865 eval.interpret_inline_policy(&Expr::is_in(
3866 Expr::val(EntityUID::with_eid("parent")),
3867 Expr::val(EntityUID::with_eid("child"))
3868 )),
3869 Ok(Value::from(false))
3870 );
3871 assert_eq!(
3873 eval.interpret_inline_policy(&Expr::is_in(
3874 Expr::val(EntityUID::with_eid("child")),
3875 Expr::val(EntityUID::with_eid("grandparent"))
3876 )),
3877 Ok(Value::from(true))
3878 );
3879 assert_eq!(
3881 eval.interpret_inline_policy(&Expr::is_in(
3882 Expr::val(EntityUID::with_eid("doesnotexist")),
3883 Expr::val(EntityUID::with_eid("parent"))
3884 )),
3885 Ok(Value::from(false))
3886 );
3887 assert_eq!(
3889 eval.interpret_inline_policy(&Expr::is_in(
3890 Expr::val(EntityUID::with_eid("parent")),
3891 Expr::val(EntityUID::with_eid("doesnotexist"))
3892 )),
3893 Ok(Value::from(false))
3894 );
3895 assert_eq!(
3897 eval.interpret_inline_policy(&Expr::is_in(
3898 Expr::val(EntityUID::with_eid("child")),
3899 Expr::set(vec![
3900 Expr::val(EntityUID::with_eid("grandparent")),
3901 Expr::val(EntityUID::with_eid("sibling")),
3902 ])
3903 )),
3904 Ok(Value::from(true))
3905 );
3906 assert_eq!(
3908 eval.interpret_inline_policy(&Expr::is_in(
3909 Expr::val(EntityUID::with_eid("child")),
3910 Expr::set(vec![
3911 Expr::val(EntityUID::with_eid("sibling")),
3912 Expr::val(EntityUID::with_eid("grandparent")),
3913 ])
3914 )),
3915 Ok(Value::from(true))
3916 );
3917 assert_eq!(
3919 eval.interpret_inline_policy(&Expr::is_in(
3920 Expr::val(EntityUID::with_eid("child")),
3921 Expr::set(vec![
3922 Expr::val(EntityUID::with_eid("sibling")),
3923 Expr::val(EntityUID::with_eid("unrelated")),
3924 ])
3925 )),
3926 Ok(Value::from(false))
3927 );
3928 assert_eq!(
3930 eval.interpret_inline_policy(&Expr::is_in(
3931 Expr::val(EntityUID::with_eid("child")),
3932 Expr::set(vec![
3933 Expr::val(EntityUID::with_eid("unrelated")),
3934 Expr::val(EntityUID::with_eid("child")),
3935 ])
3936 )),
3937 Ok(Value::from(true))
3938 );
3939 assert_eq!(
3941 eval.interpret_inline_policy(&Expr::is_in(
3942 Expr::val(EntityUID::with_eid("child")),
3943 Expr::set(vec![
3944 Expr::val(EntityUID::with_eid("child")),
3945 Expr::val(EntityUID::with_eid("unrelated")),
3946 ])
3947 )),
3948 Ok(Value::from(true))
3949 );
3950 assert_matches!(
3952 eval.interpret_inline_policy(&Expr::is_in(
3953 Expr::val(EntityUID::with_eid("child")),
3954 Expr::set(vec![
3955 Expr::val(EntityUID::with_eid("child")),
3956 Expr::val(true),
3957 ])
3958 )),
3959 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3960 assert_eq!(expected, nonempty![Type::entity_type(
3961 Name::parse_unqualified_name("any_entity_type")
3962 .expect("should be a valid identifier")
3963 )]);
3964 assert_eq!(actual, Type::Bool);
3965 assert_eq!(advice, None);
3966 }
3967 );
3968 assert_eq!(
3970 eval.interpret_inline_policy(&Expr::is_in(
3971 Expr::val(EntityUID::with_eid("doesnotexistA")),
3972 Expr::set(vec![
3973 Expr::val(EntityUID::with_eid("doesnotexistA")),
3974 Expr::val(EntityUID::with_eid("doesnotexistB")),
3975 ])
3976 )),
3977 Ok(Value::from(true))
3978 );
3979 assert_eq!(
3981 eval.interpret_inline_policy(&Expr::is_in(
3982 Expr::val(EntityUID::with_eid("doesnotexistA")),
3983 Expr::set(vec![
3984 Expr::val(EntityUID::with_eid("doesnotexistB")),
3985 Expr::val(EntityUID::with_eid("doesnotexistC")),
3986 ])
3987 )),
3988 Ok(Value::from(false))
3989 );
3990 assert_eq!(
3992 eval.interpret_inline_policy(&Expr::is_in(
3993 Expr::val(EntityUID::with_eid("child")),
3994 Expr::set(vec![
3995 Expr::val(EntityUID::with_eid("doesnotexistB")),
3996 Expr::val(EntityUID::with_eid("doesnotexistC")),
3997 ])
3998 )),
3999 Ok(Value::from(false))
4000 );
4001 assert_eq!(
4003 eval.interpret_inline_policy(&Expr::is_in(
4004 Expr::val(EntityUID::with_eid("doesnotexistA")),
4005 Expr::set(vec![
4006 Expr::val(EntityUID::with_eid("child")),
4007 Expr::val(EntityUID::with_eid("grandparent")),
4008 ])
4009 )),
4010 Ok(Value::from(false))
4011 );
4012 assert_matches!(
4014 eval.interpret_inline_policy(&Expr::is_in(Expr::val("foo"), Expr::val("foobar"))),
4015 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4016 assert_eq!(expected, nonempty![Type::entity_type(
4017 Name::parse_unqualified_name("any_entity_type")
4018 .expect("should be a valid identifier")
4019 )]);
4020 assert_eq!(actual, Type::String);
4021 assert_eq!(advice, None);
4022 }
4023 );
4024 assert_matches!(
4026 eval.interpret_inline_policy(&Expr::is_in(
4027 Expr::val("spoon"),
4028 Expr::val(EntityUID::with_eid("entity_with_attrs"))
4029 )),
4030 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4031 assert_eq!(expected, nonempty![Type::entity_type(
4032 Name::parse_unqualified_name("any_entity_type")
4033 .expect("should be a valid identifier")
4034 )]);
4035 assert_eq!(actual, Type::String);
4036 assert_eq!(advice, None);
4037 }
4038 );
4039 assert_matches!(
4041 eval.interpret_inline_policy(&Expr::is_in(
4042 Expr::val(3),
4043 Expr::set(vec![Expr::val(34), Expr::val(-2), Expr::val(7)])
4044 )),
4045 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4046 assert_eq!(expected, nonempty![Type::entity_type(
4047 Name::parse_unqualified_name("any_entity_type")
4048 .expect("should be a valid identifier")
4049 )]);
4050 assert_eq!(actual, Type::Long);
4051 assert_eq!(advice, Some("`in` is for checking the entity hierarchy; use `.contains()` to test set membership".into()));
4052 }
4053 );
4054 assert_matches!(
4056 eval.interpret_inline_policy(&Expr::is_in(
4057 Expr::val("foo"),
4058 Expr::record(vec![
4059 ("foo".into(), Expr::val(2)),
4060 ("bar".into(), Expr::val(true)),
4061 ]).unwrap()
4062 )),
4063 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4064 assert_eq!(expected, nonempty![Type::entity_type(
4065 Name::parse_unqualified_name("any_entity_type")
4066 .expect("should be a valid identifier")
4067 )]);
4068 assert_eq!(actual, Type::String);
4069 assert_eq!(advice, Some("`in` is for checking the entity hierarchy; use `has` to test if a record has a key".into()));
4070 }
4071 );
4072 assert_matches!(
4074 eval.interpret_inline_policy(&Expr::is_in(
4075 Expr::val(EntityUID::with_eid("child")),
4076 Expr::record(vec![
4077 ("foo".into(), Expr::val(2)),
4078 ("bar".into(), Expr::val(true)),
4079 ])
4080 .unwrap()
4081 )),
4082 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4083 assert_eq!(expected, nonempty![
4084 Type::Set,
4085 Type::entity_type(
4086 Name::parse_unqualified_name("any_entity_type")
4087 .expect("should be a valid identifier")
4088 )
4089 ]);
4090 assert_eq!(actual, Type::Record);
4091 assert_eq!(advice, None);
4092 }
4093 );
4094 }
4095
4096 #[test]
4097 fn interpret_hierarchy_membership_slice() {
4098 let request = Request::new(
4104 (EntityUID::with_eid("Alice"), None),
4105 (EntityUID::with_eid("test_action"), None),
4106 (EntityUID::with_eid("test_resource"), None),
4107 Context::empty(),
4108 Some(&RequestSchemaAllPass),
4109 Extensions::none(),
4110 )
4111 .unwrap();
4112 let mut alice = Entity::with_uid(EntityUID::with_eid("Alice"));
4114 let parent = Entity::with_uid(EntityUID::with_eid("Friends"));
4115 alice.add_parent(parent.uid().clone());
4116 let entities = Entities::from_entities(
4117 vec![alice],
4118 None::<&NoEntitiesSchema>,
4119 TCComputation::AssumeAlreadyComputed,
4120 Extensions::all_available(),
4121 )
4122 .expect("failed to create basic entities");
4123 let eval = Evaluator::new(request, &entities, Extensions::none());
4124 assert_eq!(
4125 eval.interpret_inline_policy(&Expr::is_in(
4126 Expr::val(EntityUID::with_eid("Alice")),
4127 Expr::val(EntityUID::with_eid("Friends"))
4128 )),
4129 Ok(Value::from(true))
4130 );
4131 assert_eq!(
4132 eval.interpret_inline_policy(&Expr::is_in(
4133 Expr::val(EntityUID::with_eid("Bob")),
4134 Expr::val(EntityUID::with_eid("Friends"))
4135 )),
4136 Ok(Value::from(false))
4137 );
4138 assert_eq!(
4139 eval.interpret_inline_policy(&Expr::is_in(
4140 Expr::val(EntityUID::with_eid("Alice")),
4141 Expr::set(vec![
4142 Expr::val(EntityUID::with_eid("Friends")),
4143 Expr::val(EntityUID::with_eid("Bob"))
4144 ])
4145 )),
4146 Ok(Value::from(true))
4147 );
4148 assert_eq!(
4149 eval.interpret_inline_policy(&Expr::is_in(
4150 Expr::val(EntityUID::with_eid("Bob")),
4151 Expr::set(vec![
4152 Expr::val(EntityUID::with_eid("Friends")),
4153 Expr::val(EntityUID::with_eid("Alice"))
4154 ])
4155 )),
4156 Ok(Value::from(false))
4157 );
4158 }
4159
4160 #[test]
4161 fn interpret_string_like() {
4162 let request = basic_request();
4163 let entities = basic_entities();
4164 let eval = Evaluator::new(request, &entities, Extensions::none());
4165 assert_eq!(
4167 eval.interpret_inline_policy(
4168 &parse_expr(r#""eggs" like "ham*""#).expect("parsing error")
4169 ),
4170 Ok(Value::from(false))
4171 );
4172 assert_eq!(
4173 eval.interpret_inline_policy(
4174 &parse_expr(r#""eggs" like "*ham""#).expect("parsing error")
4175 ),
4176 Ok(Value::from(false))
4177 );
4178 assert_eq!(
4179 eval.interpret_inline_policy(
4180 &parse_expr(r#""eggs" like "*ham*""#).expect("parsing error")
4181 ),
4182 Ok(Value::from(false))
4183 );
4184 assert_eq!(
4186 eval.interpret_inline_policy(
4187 &parse_expr(r#""ham and eggs" like "ham*""#).expect("parsing error")
4188 ),
4189 Ok(Value::from(true))
4190 );
4191 assert_eq!(
4192 eval.interpret_inline_policy(
4193 &parse_expr(r#""ham and eggs" like "*ham""#).expect("parsing error")
4194 ),
4195 Ok(Value::from(false))
4196 );
4197 assert_eq!(
4198 eval.interpret_inline_policy(
4199 &parse_expr(r#""ham and eggs" like "*ham*""#).expect("parsing error")
4200 ),
4201 Ok(Value::from(true))
4202 );
4203 assert_eq!(
4204 eval.interpret_inline_policy(
4205 &parse_expr(r#""ham and eggs" like "*h*a*m*""#).expect("parsing error")
4206 ),
4207 Ok(Value::from(true))
4208 );
4209 assert_eq!(
4211 eval.interpret_inline_policy(
4212 &parse_expr(r#""eggs and ham" like "ham*""#).expect("parsing error")
4213 ),
4214 Ok(Value::from(false))
4215 );
4216 assert_eq!(
4217 eval.interpret_inline_policy(
4218 &parse_expr(r#""eggs and ham" like "*ham""#).expect("parsing error")
4219 ),
4220 Ok(Value::from(true))
4221 );
4222 assert_eq!(
4224 eval.interpret_inline_policy(
4225 &parse_expr(r#""eggs, ham, and spinach" like "ham*""#).expect("parsing error")
4226 ),
4227 Ok(Value::from(false))
4228 );
4229 assert_eq!(
4230 eval.interpret_inline_policy(
4231 &parse_expr(r#""eggs, ham, and spinach" like "*ham""#).expect("parsing error")
4232 ),
4233 Ok(Value::from(false))
4234 );
4235 assert_eq!(
4236 eval.interpret_inline_policy(
4237 &parse_expr(r#""eggs, ham, and spinach" like "*ham*""#).expect("parsing error")
4238 ),
4239 Ok(Value::from(true))
4240 );
4241 assert_eq!(
4243 eval.interpret_inline_policy(
4244 &parse_expr(r#""Gotham" like "ham*""#).expect("parsing error")
4245 ),
4246 Ok(Value::from(false))
4247 );
4248 assert_eq!(
4249 eval.interpret_inline_policy(
4250 &parse_expr(r#""Gotham" like "*ham""#).expect("parsing error")
4251 ),
4252 Ok(Value::from(true))
4253 );
4254 assert_eq!(
4256 eval.interpret_inline_policy(
4257 &parse_expr(r#""ham" like "ham""#).expect("parsing error")
4258 ),
4259 Ok(Value::from(true))
4260 );
4261 assert_eq!(
4262 eval.interpret_inline_policy(
4263 &parse_expr(r#""ham" like "ham*""#).expect("parsing error")
4264 ),
4265 Ok(Value::from(true))
4266 );
4267 assert_eq!(
4268 eval.interpret_inline_policy(
4269 &parse_expr(r#""ham" like "*ham""#).expect("parsing error")
4270 ),
4271 Ok(Value::from(true))
4272 );
4273 assert_eq!(
4274 eval.interpret_inline_policy(
4275 &parse_expr(r#""ham" like "*h*a*m*""#).expect("parsing error")
4276 ),
4277 Ok(Value::from(true))
4278 );
4279 assert_eq!(
4281 eval.interpret_inline_policy(
4282 &parse_expr(r#""ham and ham" like "ham*""#).expect("parsing error")
4283 ),
4284 Ok(Value::from(true))
4285 );
4286 assert_eq!(
4287 eval.interpret_inline_policy(
4288 &parse_expr(r#""ham and ham" like "*ham""#).expect("parsing error")
4289 ),
4290 Ok(Value::from(true))
4291 );
4292 assert_eq!(
4294 eval.interpret_inline_policy(
4295 &parse_expr(r#""ham" like "*ham and eggs*""#).expect("parsing error")
4296 ),
4297 Ok(Value::from(false))
4298 );
4299 assert_matches!(
4301 eval.interpret_inline_policy(&Expr::like(Expr::val(354), Pattern::from(vec![]))),
4302 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4303 assert_eq!(expected, nonempty![Type::String]);
4304 assert_eq!(actual, Type::Long);
4305 assert_eq!(advice, None);
4306 }
4307 );
4308 assert_matches!(
4310 eval.interpret_inline_policy(&Expr::contains(
4311 Expr::val("ham and ham"),
4312 Expr::val("ham")
4313 )),
4314 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4315 assert_eq!(expected, nonempty![Type::Set]);
4316 assert_eq!(actual, Type::String);
4317 assert_eq!(advice, None);
4318 }
4319 );
4320 assert_eq!(
4322 eval.interpret_inline_policy(&Expr::like(
4323 Expr::val("*"),
4324 Pattern::from(vec![PatternElem::Char('\u{0000}')])
4325 )),
4326 Ok(Value::from(false))
4327 );
4328
4329 assert_eq!(
4330 eval.interpret_inline_policy(
4331 &parse_expr(r#" "\\afterslash" like "\\*" "#).expect("parsing error")
4332 ),
4333 Ok(Value::from(true))
4334 );
4335 }
4336
4337 #[test]
4338 fn interpret_string_like_escaped_chars() {
4339 let request = basic_request();
4340 let entities = basic_entities();
4341 let eval = Evaluator::new(request, &entities, Extensions::none());
4342 assert_eq!(
4344 eval.interpret_inline_policy(
4345 &parse_expr(r#""string\\with\\backslashes" like "string\\with\\backslashes""#)
4346 .expect("parsing error")
4347 ),
4348 Ok(Value::from(true))
4349 );
4350 assert_eq!(
4351 eval.interpret_inline_policy(
4352 &parse_expr(
4353 r#""string\\with\\backslashes" like "string\u{0000}with\u{0000}backslashe""#
4354 )
4355 .expect("parsing error")
4356 ),
4357 Ok(Value::from(false))
4358 );
4359 assert_eq!(
4360 eval.interpret_inline_policy(
4361 &parse_expr(r#""string\\with\\backslashes" like "string*with*backslashes""#)
4362 .expect("parsing error")
4363 ),
4364 Ok(Value::from(true))
4365 );
4366 assert_eq!(
4367 eval.interpret_inline_policy(
4368 &parse_expr(r#""string*with*stars" like "string\*with\*stars""#)
4369 .expect("parsing error")
4370 ),
4371 Ok(Value::from(true))
4372 );
4373 assert_eq!(eval.interpret_inline_policy(&parse_expr(r#""string\\*with\\*backslashes\\*and\\*stars" like "string\\*with\\*backslashes\\*and\\*stars""#).expect("parsing error")), Ok(Value::from(true)));
4374 }
4375
4376 #[test]
4377 fn interpret_is() {
4378 let request = basic_request();
4379 let entities = basic_entities();
4380 let eval = Evaluator::new(request, &entities, Extensions::none());
4381 assert_eq!(
4382 eval.interpret_inline_policy(
4383 &parse_expr(&format!(
4384 r#"principal is {}"#,
4385 EntityUID::test_entity_type()
4386 ))
4387 .expect("parsing error")
4388 ),
4389 Ok(Value::from(true))
4390 );
4391 assert_eq!(
4392 eval.interpret_inline_policy(
4393 &parse_expr(&format!(
4394 r#"principal is N::S::{}"#,
4395 EntityUID::test_entity_type()
4396 ))
4397 .expect("parsing error")
4398 ),
4399 Ok(Value::from(false))
4400 );
4401 assert_eq!(
4402 eval.interpret_inline_policy(
4403 &parse_expr(r#"User::"alice" is User"#).expect("parsing error")
4404 ),
4405 Ok(Value::from(true))
4406 );
4407 assert_eq!(
4408 eval.interpret_inline_policy(
4409 &parse_expr(r#"User::"alice" is Group"#).expect("parsing error")
4410 ),
4411 Ok(Value::from(false))
4412 );
4413 assert_eq!(
4414 eval.interpret_inline_policy(
4415 &parse_expr(r#"N::S::User::"alice" is N::S::User"#).expect("parsing error")
4416 ),
4417 Ok(Value::from(true))
4418 );
4419 assert_eq!(
4420 eval.interpret_inline_policy(
4421 &parse_expr(r#"N::S::User::"alice" is User"#).expect("parsing error")
4422 ),
4423 Ok(Value::from(false))
4424 );
4425 assert_matches!(
4426 eval.interpret_inline_policy(&parse_expr(r#"1 is Group"#).expect("parsing error")),
4427 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4428 assert_eq!(expected, nonempty![Type::entity_type(names::ANY_ENTITY_TYPE.clone())]);
4429 assert_eq!(actual, Type::Long);
4430 assert_eq!(advice, None);
4431 }
4432 );
4433 }
4434
4435 #[test]
4436 fn interpret_is_empty() {
4437 let request = basic_request();
4438 let entities = basic_entities();
4439 let eval = Evaluator::new(request, &entities, Extensions::none());
4440 assert_eq!(
4442 eval.interpret_inline_policy(&Expr::is_empty(Expr::set([]),)),
4443 Ok(Value::from(true))
4444 );
4445 assert_eq!(
4447 eval.interpret_inline_policy(&Expr::is_empty(Expr::set(vec![Expr::val(1)]),)),
4448 Ok(Value::from(false))
4449 );
4450 assert_eq!(
4452 eval.interpret_inline_policy(&Expr::is_empty(Expr::set(vec![Expr::val(false)]),)),
4453 Ok(Value::from(false))
4454 );
4455 assert_eq!(
4457 eval.interpret_inline_policy(&Expr::is_empty(Expr::set(vec![
4458 Expr::val(1),
4459 Expr::val(2),
4460 Expr::val(3),
4461 Expr::val(4),
4462 Expr::val(5),
4463 Expr::val(EntityUID::with_eid("jane"))
4464 ]))),
4465 Ok(Value::from(false))
4466 );
4467 assert_matches!(
4469 eval.interpret_inline_policy(&Expr::is_empty(
4470 Expr::val(0)
4471 )),
4472 Err(e) => {
4473 expect_err(
4474 "",
4475 &miette::Report::new(e),
4476 &ExpectedErrorMessageBuilder::error("type error: expected set, got long").build(),
4477 );
4478 }
4479 );
4480 assert_matches!(
4482 eval.interpret_inline_policy(&Expr::is_empty(
4483 Expr::record([
4484 ("foo".into(), Expr::set([]))
4485 ]).unwrap()
4486 )),
4487 Err(e) => {
4488 expect_err(
4489 "",
4490 &miette::Report::new(e),
4491 &ExpectedErrorMessageBuilder::error("type error: expected set, got record").build(),
4492 );
4493 }
4494 );
4495 }
4496
4497 #[test]
4498 fn interpret_contains_all_and_contains_any() {
4499 let request = basic_request();
4500 let entities = basic_entities();
4501 let eval = Evaluator::new(request, &entities, Extensions::none());
4502 assert_eq!(
4504 eval.interpret_inline_policy(&Expr::contains_all(
4505 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
4506 Expr::set(vec![Expr::val(1), Expr::val(-22)])
4507 )),
4508 Ok(Value::from(true))
4509 );
4510 assert_eq!(
4512 eval.interpret_inline_policy(&Expr::contains_all(
4513 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
4514 Expr::set(vec![Expr::val(-22), Expr::val(1)])
4515 )),
4516 Ok(Value::from(true))
4517 );
4518 assert_eq!(
4520 eval.interpret_inline_policy(&Expr::contains_all(
4521 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
4522 Expr::set(vec![Expr::val(-22)])
4523 )),
4524 Ok(Value::from(true))
4525 );
4526 assert_eq!(
4528 eval.interpret_inline_policy(&Expr::contains_all(
4529 Expr::set(vec![Expr::val(43), Expr::val(34)]),
4530 Expr::set(vec![Expr::val(34), Expr::val(43)])
4531 )),
4532 Ok(Value::from(true))
4533 );
4534 assert_eq!(
4536 eval.interpret_inline_policy(&Expr::contains_all(
4537 Expr::set(vec![Expr::val(1), Expr::val(-2), Expr::val(34)]),
4538 Expr::set(vec![Expr::val(1), Expr::val(-22)])
4539 )),
4540 Ok(Value::from(false))
4541 );
4542 assert_eq!(
4544 eval.interpret_inline_policy(&Expr::contains_all(
4545 Expr::set(vec![Expr::val(1), Expr::val(34)]),
4546 Expr::set(vec![Expr::val(1), Expr::val(101), Expr::val(34)])
4547 )),
4548 Ok(Value::from(false))
4549 );
4550 assert_eq!(
4552 eval.interpret_inline_policy(&Expr::contains_all(
4553 Expr::set(vec![Expr::val(1), Expr::val(34), Expr::val(102)]),
4554 Expr::set(vec![Expr::val(1), Expr::val(101), Expr::val(34)])
4555 )),
4556 Ok(Value::from(false))
4557 );
4558 assert_eq!(
4560 eval.interpret_inline_policy(&Expr::contains_all(
4561 Expr::set(vec![Expr::val(2), Expr::val(-7), Expr::val(387)]),
4562 Expr::set(vec![Expr::val(1), Expr::val(101), Expr::val(34)])
4563 )),
4564 Ok(Value::from(false))
4565 );
4566 assert_eq!(
4568 eval.interpret_inline_policy(&Expr::contains_all(
4569 Expr::set(vec![Expr::val(2), Expr::val(43)]),
4570 Expr::set(vec![])
4571 )),
4572 Ok(Value::from(true))
4573 );
4574 assert_eq!(
4576 eval.interpret_inline_policy(&Expr::contains_all(
4577 Expr::set(vec![]),
4578 Expr::set(vec![Expr::val(2), Expr::val(43)])
4579 )),
4580 Ok(Value::from(false))
4581 );
4582 assert_eq!(
4584 eval.interpret_inline_policy(&Expr::contains_all(
4585 Expr::set(vec![
4586 Expr::val(EntityUID::with_eid("bar")),
4587 Expr::val(EntityUID::with_eid("foo"))
4588 ]),
4589 Expr::set(vec![Expr::val(EntityUID::with_eid("foo"))])
4590 )),
4591 Ok(Value::from(true))
4592 );
4593 assert_eq!(
4595 eval.interpret_inline_policy(&Expr::contains_all(
4596 Expr::set(vec![
4597 Expr::val(false),
4598 Expr::val(3),
4599 Expr::set(vec![Expr::val(47), Expr::val(0)]),
4600 Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap()
4601 ]),
4602 Expr::set(vec![
4603 Expr::val(3),
4604 Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap()
4605 ])
4606 )),
4607 Ok(Value::from(true))
4608 );
4609 assert_matches!(
4611 eval.interpret_inline_policy(&Expr::contains_all(
4612 Expr::val("ham"),
4613 Expr::val("ham and eggs")
4614 )),
4615 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4616 assert_eq!(expected, nonempty![Type::Set]);
4617 assert_eq!(actual, Type::String);
4618 assert_eq!(advice, None);
4619 }
4620 );
4621 assert_matches!(
4623 eval.interpret_inline_policy(&Expr::contains_all(
4624 Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap(),
4625 Expr::record(vec![
4626 ("2".into(), Expr::val("ham")),
4627 ("3".into(), Expr::val("eggs"))
4628 ])
4629 .unwrap()
4630 )),
4631 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4632 assert_eq!(expected, nonempty![Type::Set]);
4633 assert_eq!(actual, Type::Record);
4634 assert_eq!(advice, None);
4635 }
4636 );
4637 assert_eq!(
4639 eval.interpret_inline_policy(&Expr::contains_any(
4640 Expr::set(vec![Expr::val(1), Expr::val(-22)]),
4641 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)])
4642 )),
4643 Ok(Value::from(true))
4644 );
4645 assert_eq!(
4647 eval.interpret_inline_policy(&Expr::contains_any(
4648 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
4649 Expr::set(vec![Expr::val(1), Expr::val(-22)])
4650 )),
4651 Ok(Value::from(true))
4652 );
4653 assert_eq!(
4655 eval.interpret_inline_policy(&Expr::contains_any(
4656 Expr::set(vec![Expr::val(-22)]),
4657 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)])
4658 )),
4659 Ok(Value::from(true))
4660 );
4661 assert_eq!(
4663 eval.interpret_inline_policy(&Expr::contains_any(
4664 Expr::set(vec![Expr::val(1), Expr::val(101)]),
4665 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)])
4666 )),
4667 Ok(Value::from(true))
4668 );
4669 assert_eq!(
4671 eval.interpret_inline_policy(&Expr::contains_any(
4672 Expr::set(vec![Expr::val(1), Expr::val(101)]),
4673 Expr::set(vec![Expr::val(-22), Expr::val(34)])
4674 )),
4675 Ok(Value::from(false))
4676 );
4677 assert_eq!(
4679 eval.interpret_inline_policy(&Expr::contains_any(
4680 Expr::set(vec![]),
4681 Expr::set(vec![Expr::val(-22), Expr::val(34)])
4682 )),
4683 Ok(Value::from(false))
4684 );
4685 assert_eq!(
4687 eval.interpret_inline_policy(&Expr::contains_any(
4688 Expr::set(vec![Expr::val(-22), Expr::val(34)]),
4689 Expr::set(vec![])
4690 )),
4691 Ok(Value::from(false))
4692 );
4693 assert_eq!(
4695 eval.interpret_inline_policy(&Expr::contains_any(
4696 Expr::set(vec![
4697 Expr::val(EntityUID::with_eid("foo")),
4698 Expr::val(EntityUID::with_eid("bar"))
4699 ]),
4700 Expr::set(vec![
4701 Expr::val(EntityUID::with_eid("ham")),
4702 Expr::val(EntityUID::with_eid("eggs"))
4703 ])
4704 )),
4705 Ok(Value::from(false))
4706 );
4707 assert_eq!(
4709 eval.interpret_inline_policy(&Expr::contains_any(
4710 Expr::set(vec![
4711 Expr::val(3),
4712 Expr::record(vec![
4713 ("2".into(), Expr::val("ham")),
4714 ("1".into(), Expr::val("eggs"))
4715 ])
4716 .unwrap()
4717 ]),
4718 Expr::set(vec![
4719 Expr::val(7),
4720 Expr::val(false),
4721 Expr::set(vec![Expr::val(-22), Expr::val(true)]),
4722 Expr::record(vec![
4723 ("1".into(), Expr::val("eggs")),
4724 ("2".into(), Expr::val("ham"))
4725 ])
4726 .unwrap()
4727 ])
4728 )),
4729 Ok(Value::from(true))
4730 );
4731 assert_matches!(
4733 eval.interpret_inline_policy(&Expr::contains_any(
4734 Expr::val("ham"),
4735 Expr::val("ham and eggs")
4736 )),
4737 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4738 assert_eq!(expected, nonempty![Type::Set]);
4739 assert_eq!(actual, Type::String);
4740 assert_eq!(advice, None);
4741 }
4742 );
4743 assert_matches!(
4745 eval.interpret_inline_policy(&Expr::contains_any(
4746 Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap(),
4747 Expr::record(vec![
4748 ("2".into(), Expr::val("ham")),
4749 ("3".into(), Expr::val("eggs"))
4750 ])
4751 .unwrap()
4752 )),
4753 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4754 assert_eq!(expected, nonempty![Type::Set]);
4755 assert_eq!(actual, Type::Record);
4756 assert_eq!(advice, None);
4757 }
4758 );
4759 }
4760
4761 #[test]
4762 fn eval_and_or() -> Result<()> {
4763 use crate::parser;
4764 let request = basic_request();
4765 let eparser: EntityJsonParser<'_, '_> =
4766 EntityJsonParser::new(None, Extensions::none(), TCComputation::ComputeNow);
4767 let entities = eparser.from_json_str("[]").expect("empty slice");
4768 let evaluator = Evaluator::new(request, &entities, Extensions::none());
4769
4770 let raw_expr = "(false && 3)";
4772 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4773 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4774
4775 let raw_expr = "(true || 3)";
4776 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4777 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4778
4779 let raw_expr = "(false && 3) == 3";
4781 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4782 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4783
4784 let raw_expr = "(true || 3) == 3";
4785 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4786 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4787
4788 let raw_expr = "(false && 3 && true) == 3";
4789 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4790 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4791
4792 let raw_expr = "(true || 3 || true) == 3";
4793 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4794 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4795
4796 let raw_expr = "(true && 3)";
4798 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4799 let t = evaluator.interpret_inline_policy(&expr);
4800 println!("EXPR={t:?}");
4801 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4802
4803 let raw_expr = "(3 && true)";
4804 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4805 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4806
4807 let raw_expr = "(3 && false)";
4808 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4809 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4810
4811 let raw_expr = "(3 || true)";
4812 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4813 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4814
4815 let raw_expr = "(3 || false)";
4816 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4817 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4818
4819 let raw_expr = "(false || 3)";
4820 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4821 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4822
4823 let raw_expr = "(true && 3) == 3";
4824 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4825 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4826
4827 let raw_expr = "(3 && true) == 3";
4828 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4829 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4830
4831 let raw_expr = "(3 && false) == 3";
4832 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4833 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4834
4835 let raw_expr = "(3 || true) == 3";
4836 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4837 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4838
4839 let raw_expr = "(3 || false) == 3";
4840 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4841 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4842
4843 let raw_expr = "(false || 3) == 3";
4844 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4845 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4846
4847 let raw_expr = "(true && 3 && true) == 3";
4848 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4849 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4850
4851 let raw_expr = "(3 && true && true) == 3";
4852 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4853 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4854
4855 let raw_expr = "(3 && false && true) == 3";
4856 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4857 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4858
4859 let raw_expr = "(3 || true || true) == 3";
4860 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4861 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4862
4863 let raw_expr = "(3 || false || true) == 3";
4864 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4865 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4866
4867 let raw_expr = "(false || 3 || true) == 3";
4868 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4869 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4870
4871 Ok(())
4872 }
4873
4874 #[test]
4875 fn template_env_tests() {
4876 let request = basic_request();
4877 let eparser: EntityJsonParser<'_, '_> =
4878 EntityJsonParser::new(None, Extensions::none(), TCComputation::ComputeNow);
4879 let entities = eparser.from_json_str("[]").expect("empty slice");
4880 let evaluator = Evaluator::new(request, &entities, Extensions::none());
4881 let e = Expr::slot(SlotId::principal());
4882
4883 let slots = HashMap::new();
4884 let r = evaluator.partial_interpret(&e, &slots);
4885 assert_matches!(r, Err(EvaluationError::UnlinkedSlot(UnlinkedSlotError { slot, .. })) => {
4886 assert_eq!(slot, SlotId::principal());
4887 });
4888
4889 let mut slots = HashMap::new();
4890 slots.insert(SlotId::principal(), EntityUID::with_eid("eid"));
4891 let r = evaluator.partial_interpret(&e, &slots);
4892 assert_matches!(r, Ok(e) => {
4893 assert_eq!(
4894 e,
4895 PartialValue::Value(Value::from(
4896 EntityUID::with_eid("eid")
4897 ))
4898 );
4899 });
4900 }
4901
4902 #[test]
4903 fn template_interp() {
4904 let t = parse_policy_or_template(
4905 Some(PolicyID::from_string("template")),
4906 r#"permit(principal == ?principal, action, resource);"#,
4907 )
4908 .expect("Parse Error");
4909 let mut pset = PolicySet::new();
4910 pset.add_template(t)
4911 .expect("Template already present in PolicySet");
4912 let mut values = HashMap::new();
4913 values.insert(SlotId::principal(), EntityUID::with_eid("p"));
4914 pset.link(
4915 PolicyID::from_string("template"),
4916 PolicyID::from_string("instance"),
4917 values,
4918 )
4919 .expect("Linking failed!");
4920 let q = Request::new(
4921 (EntityUID::with_eid("p"), None),
4922 (EntityUID::with_eid("a"), None),
4923 (EntityUID::with_eid("r"), None),
4924 Context::empty(),
4925 Some(&RequestSchemaAllPass),
4926 Extensions::none(),
4927 )
4928 .unwrap();
4929 let eparser: EntityJsonParser<'_, '_> =
4930 EntityJsonParser::new(None, Extensions::none(), TCComputation::ComputeNow);
4931 let entities = eparser.from_json_str("[]").expect("empty slice");
4932 let eval = Evaluator::new(q, &entities, Extensions::none());
4933
4934 let ir = pset.policies().next().expect("No linked policies");
4935 assert_matches!(eval.partial_evaluate(ir), Ok(Either::Left(b)) => {
4936 assert!(b, "Should be enforced");
4937 });
4938 }
4939
4940 #[track_caller] fn assert_restricted_expression_error(e: &Expr) {
4942 assert_matches!(
4943 BorrowedRestrictedExpr::new(e),
4944 Err(RestrictedExpressionError::InvalidRestrictedExpression { .. })
4945 );
4946 }
4947
4948 #[test]
4949 fn restricted_expressions() {
4950 let evaluator = RestrictedEvaluator::new(Extensions::all_available());
4951
4952 assert_eq!(
4954 evaluator.partial_interpret(BorrowedRestrictedExpr::new(&Expr::val(true)).unwrap()),
4955 Ok(Value::from(true).into())
4956 );
4957 assert_eq!(
4958 evaluator.partial_interpret(BorrowedRestrictedExpr::new(&Expr::val(-2)).unwrap()),
4959 Ok(Value::from(-2).into())
4960 );
4961 assert_eq!(
4962 evaluator
4963 .partial_interpret(BorrowedRestrictedExpr::new(&Expr::val("hello world")).unwrap()),
4964 Ok(Value::from("hello world").into())
4965 );
4966 assert_eq!(
4967 evaluator.partial_interpret(
4968 BorrowedRestrictedExpr::new(&Expr::val(EntityUID::with_eid("alice"))).unwrap()
4969 ),
4970 Ok(Value::from(EntityUID::with_eid("alice")).into())
4971 );
4972 assert_restricted_expression_error(&Expr::var(Var::Principal));
4973 assert_restricted_expression_error(&Expr::var(Var::Action));
4974 assert_restricted_expression_error(&Expr::var(Var::Resource));
4975 assert_restricted_expression_error(&Expr::var(Var::Context));
4976 assert_restricted_expression_error(&Expr::ite(
4977 Expr::val(true),
4978 Expr::val(7),
4979 Expr::val(12),
4980 ));
4981 assert_restricted_expression_error(&Expr::and(Expr::val("bogus"), Expr::val(true)));
4982 assert_restricted_expression_error(&Expr::or(Expr::val("bogus"), Expr::val(true)));
4983 assert_restricted_expression_error(&Expr::not(Expr::val(true)));
4984 assert_restricted_expression_error(&Expr::is_in(
4985 Expr::val(EntityUID::with_eid("alice")),
4986 Expr::val(EntityUID::with_eid("some_group")),
4987 ));
4988 assert_restricted_expression_error(&Expr::is_eq(
4989 Expr::val(EntityUID::with_eid("alice")),
4990 Expr::val(EntityUID::with_eid("some_group")),
4991 ));
4992 #[cfg(feature = "ipaddr")]
4993 assert_matches!(
4994 evaluator.partial_interpret(
4995 BorrowedRestrictedExpr::new(&Expr::call_extension_fn(
4996 "ip".parse().expect("should be a valid Name"),
4997 vec![Expr::val("222.222.222.222")]
4998 ))
4999 .unwrap()
5000 ),
5001 Ok(PartialValue::Value(Value {
5002 value: ValueKind::ExtensionValue(_),
5003 ..
5004 }))
5005 );
5006 assert_restricted_expression_error(&Expr::get_attr(
5007 Expr::val(EntityUID::with_eid("alice")),
5008 "pancakes".into(),
5009 ));
5010 assert_restricted_expression_error(&Expr::has_attr(
5011 Expr::val(EntityUID::with_eid("alice")),
5012 "pancakes".into(),
5013 ));
5014 assert_restricted_expression_error(&Expr::like(
5015 Expr::val("abcdefg12"),
5016 Pattern::from(vec![
5017 PatternElem::Char('a'),
5018 PatternElem::Char('b'),
5019 PatternElem::Char('c'),
5020 PatternElem::Wildcard,
5021 ]),
5022 ));
5023 assert_matches!(
5024 evaluator.partial_interpret(
5025 BorrowedRestrictedExpr::new(&Expr::set([Expr::val("hi"), Expr::val("there")]))
5026 .unwrap()
5027 ),
5028 Ok(PartialValue::Value(Value {
5029 value: ValueKind::Set(_),
5030 ..
5031 }))
5032 );
5033 assert_matches!(
5034 evaluator.partial_interpret(
5035 BorrowedRestrictedExpr::new(
5036 &Expr::record([
5037 ("hi".into(), Expr::val(1001)),
5038 ("foo".into(), Expr::val("bar"))
5039 ])
5040 .unwrap()
5041 )
5042 .unwrap()
5043 ),
5044 Ok(PartialValue::Value(Value {
5045 value: ValueKind::Record(_),
5046 ..
5047 }))
5048 );
5049
5050 assert_restricted_expression_error(&Expr::set([
5052 Expr::val("hi"),
5053 Expr::and(Expr::val("bogus"), Expr::val(false)),
5054 ]));
5055 assert_restricted_expression_error(&Expr::call_extension_fn(
5056 "ip".parse().expect("should be a valid Name"),
5057 vec![Expr::var(Var::Principal)],
5058 ));
5059
5060 assert_restricted_expression_error(&Expr::is_entity_type(
5061 Expr::val(EntityUID::with_eid("alice")),
5062 "User".parse().unwrap(),
5063 ));
5064 }
5065
5066 #[test]
5067 fn simple_partial() {
5068 let pset = parse_policyset(
5069 r#"
5070 permit(principal == Principal::"alice", action, resource);
5071 "#,
5072 )
5073 .expect("Failed to parse");
5074 let euid =
5075 Arc::new(EntityUID::from_str(r#"Principal::"alice""#).expect("EUID failed to parse"));
5076 let p = pset
5077 .get(&PolicyID::from_string("policy0"))
5078 .expect("No such policy");
5079 let q = Request::new_with_unknowns(
5080 EntityUIDEntry::unknown(),
5081 EntityUIDEntry::unknown(),
5082 EntityUIDEntry::unknown(),
5083 Some(Context::empty()),
5084 Some(&RequestSchemaAllPass),
5085 Extensions::none(),
5086 )
5087 .unwrap();
5088 let es = Entities::new();
5089 let e = Evaluator::new(q, &es, Extensions::none());
5090 match e.partial_evaluate(p).expect("eval error") {
5091 Either::Left(_) => panic!("Evalled to a value"),
5092 Either::Right(expr) => {
5093 println!("{expr}");
5094 assert!(expr.contains_unknown());
5095 let m: HashMap<_, _> = HashMap::from([("principal".into(), Value::from(euid))]);
5096 let new_expr = expr.substitute_typed(&m).unwrap();
5097 assert_eq!(
5098 e.partial_interpret(&new_expr, &HashMap::new())
5099 .expect("Failed to eval"),
5100 PartialValue::Value(true.into())
5101 );
5102 }
5103 }
5104 }
5105
5106 fn partial_context_test(context_expr: Expr, e: &Expr) -> Either<Value, Expr> {
5107 let euid: EntityUID = r#"Test::"test""#.parse().unwrap();
5108 let rexpr = RestrictedExpr::new(context_expr)
5109 .expect("Context Expression was not a restricted expression");
5110 let context = Context::from_expr(rexpr.as_borrowed(), Extensions::none()).unwrap();
5111 let q = Request::new(
5112 (euid.clone(), None),
5113 (euid.clone(), None),
5114 (euid, None),
5115 context,
5116 Some(&RequestSchemaAllPass),
5117 Extensions::none(),
5118 )
5119 .unwrap();
5120 let es = Entities::new();
5121 let eval = Evaluator::new(q, &es, Extensions::none());
5122 eval.partial_eval_expr(e).unwrap()
5123 }
5124
5125 #[test]
5126 fn partial_contexts1() {
5127 let c_expr =
5129 Expr::record([("cell".into(), Expr::unknown(Unknown::new_untyped("cell")))]).unwrap();
5130 let expr = Expr::binary_app(
5131 BinaryOp::Eq,
5132 Expr::get_attr(Expr::var(Var::Context), "cell".into()),
5133 Expr::val(2),
5134 );
5135 let expected = Expr::binary_app(
5136 BinaryOp::Eq,
5137 Expr::unknown(Unknown::new_untyped("cell")),
5138 Expr::val(2),
5139 );
5140
5141 let r = partial_context_test(c_expr, &expr);
5142
5143 assert_eq!(r, Either::Right(expected));
5144 }
5145
5146 #[test]
5147 fn partial_contexts2() {
5148 let c_expr = Expr::record([
5150 ("loc".into(), Expr::val("test")),
5151 ("cell".into(), Expr::unknown(Unknown::new_untyped("cell"))),
5152 ])
5153 .unwrap();
5154 let expr = Expr::binary_app(
5156 BinaryOp::Eq,
5157 Expr::get_attr(Expr::var(Var::Context), "cell".into()),
5158 Expr::val(2),
5159 );
5160 let r = partial_context_test(c_expr.clone(), &expr);
5161 let expected = Expr::binary_app(
5162 BinaryOp::Eq,
5163 Expr::unknown(Unknown::new_untyped("cell")),
5164 Expr::val(2),
5165 );
5166 assert_eq!(r, Either::Right(expected));
5167
5168 let expr = Expr::binary_app(
5170 BinaryOp::Eq,
5171 Expr::get_attr(Expr::var(Var::Context), "loc".into()),
5172 Expr::val(2),
5173 );
5174 let r = partial_context_test(c_expr, &expr);
5175 assert_eq!(r, Either::Left(false.into()));
5176 }
5177
5178 #[test]
5179 fn partial_contexts3() {
5180 let row =
5182 Expr::record([("row".into(), Expr::unknown(Unknown::new_untyped("row")))]).unwrap();
5183 let c_expr =
5185 Expr::record([("loc".into(), Expr::val("test")), ("cell".into(), row)]).unwrap();
5186 let expr = Expr::binary_app(
5189 BinaryOp::Eq,
5190 Expr::get_attr(
5191 Expr::get_attr(Expr::var(Var::Context), "cell".into()),
5192 "row".into(),
5193 ),
5194 Expr::val(2),
5195 );
5196 let r = partial_context_test(c_expr, &expr);
5197 let expected = Expr::binary_app(
5198 BinaryOp::Eq,
5199 Expr::unknown(Unknown::new_untyped("row")),
5200 Expr::val(2),
5201 );
5202 assert_eq!(r, Either::Right(expected));
5203 }
5204
5205 #[test]
5206 fn partial_contexts4() {
5207 let row = Expr::record([
5209 ("row".into(), Expr::unknown(Unknown::new_untyped("row"))),
5210 ("col".into(), Expr::unknown(Unknown::new_untyped("col"))),
5211 ])
5212 .unwrap();
5213 let c_expr =
5215 Expr::record([("loc".into(), Expr::val("test")), ("cell".into(), row)]).unwrap();
5216 let expr = Expr::binary_app(
5219 BinaryOp::Eq,
5220 Expr::get_attr(
5221 Expr::get_attr(Expr::var(Var::Context), "cell".into()),
5222 "row".into(),
5223 ),
5224 Expr::val(2),
5225 );
5226 let r = partial_context_test(c_expr.clone(), &expr);
5227 let expected = Expr::binary_app(
5228 BinaryOp::Eq,
5229 Expr::unknown(Unknown::new_untyped("row")),
5230 Expr::val(2),
5231 );
5232 assert_eq!(r, Either::Right(expected));
5233 let expr = Expr::binary_app(
5235 BinaryOp::Eq,
5236 Expr::get_attr(
5237 Expr::get_attr(Expr::var(Var::Context), "cell".into()),
5238 "col".into(),
5239 ),
5240 Expr::val(2),
5241 );
5242 let r = partial_context_test(c_expr, &expr);
5243 let expected = Expr::binary_app(
5244 BinaryOp::Eq,
5245 Expr::unknown(Unknown::new_untyped("col")),
5246 Expr::val(2),
5247 );
5248 assert_eq!(r, Either::Right(expected));
5249 }
5250
5251 #[test]
5252 fn partial_context_fail() {
5253 let context = Context::from_expr(
5254 RestrictedExpr::new_unchecked(
5255 Expr::record([
5256 ("a".into(), Expr::val(3)),
5257 ("b".into(), Expr::unknown(Unknown::new_untyped("b"))),
5258 ])
5259 .unwrap(),
5260 )
5261 .as_borrowed(),
5262 Extensions::none(),
5263 )
5264 .unwrap();
5265 let euid: EntityUID = r#"Test::"test""#.parse().unwrap();
5266 let q = Request::new(
5267 (euid.clone(), None),
5268 (euid.clone(), None),
5269 (euid, None),
5270 context,
5271 Some(&RequestSchemaAllPass),
5272 Extensions::none(),
5273 )
5274 .unwrap();
5275 let es = Entities::new();
5276 let eval = Evaluator::new(q, &es, Extensions::none());
5277 let e = Expr::get_attr(Expr::var(Var::Context), "foo".into());
5278 assert_matches!(eval.partial_eval_expr(&e), Err(_))
5279 }
5280
5281 #[test]
5282 fn mikes_test() {
5283 let policyset = parse_policyset(
5284 r#"
5285 permit(
5286 principal == Principal::"p",
5287 action == Action::"a",
5288 resource == Table::"t"
5289 ) when {
5290 context.cell.row > 5 && context.cell.col < 2
5291 };
5292 "#,
5293 )
5294 .expect("Failed to parse");
5295 let policy = policyset
5296 .get(&PolicyID::from_string("policy0"))
5297 .expect("No such policy");
5298
5299 let es = Entities::new();
5300
5301 let p: EntityUID = r#"Principal::"p""#.parse().expect("Failed to parse");
5302 let a: EntityUID = r#"Action::"a""#.parse().expect("Failed to parse");
5303 let r: EntityUID = r#"Table::"t""#.parse().expect("Failed to parse");
5304
5305 let c_expr = RestrictedExpr::new(
5306 Expr::record([("cell".into(), Expr::unknown(Unknown::new_untyped("cell")))]).unwrap(),
5307 )
5308 .expect("should qualify as restricted");
5309 let context = Context::from_expr(c_expr.as_borrowed(), Extensions::none()).unwrap();
5310
5311 let q = Request::new(
5312 (p, None),
5313 (a, None),
5314 (r, None),
5315 context,
5316 Some(&RequestSchemaAllPass),
5317 Extensions::none(),
5318 )
5319 .unwrap();
5320 let eval = Evaluator::new(q, &es, Extensions::none());
5321
5322 let result = eval.partial_evaluate(policy).expect("Eval error");
5323 match result {
5324 Either::Left(_) => panic!("Got a value"),
5325 Either::Right(r) => {
5326 println!("{r}");
5327 }
5328 }
5329 }
5330
5331 fn empty_request() -> Request {
5332 let p: EntityUID = r#"p::"Principal""#.parse().unwrap();
5333 let a: EntityUID = r#"a::"Action""#.parse().unwrap();
5334 let r: EntityUID = r#"r::"Resource""#.parse().unwrap();
5335 let c = Context::empty();
5336 Request::new(
5337 (p, None),
5338 (a, None),
5339 (r, None),
5340 c,
5341 Some(&RequestSchemaAllPass),
5342 Extensions::none(),
5343 )
5344 .unwrap()
5345 }
5346
5347 #[test]
5348 fn if_semantics_residual_guard() {
5349 let a = Expr::unknown(Unknown::new_untyped("guard"));
5350 let b = Expr::and(Expr::val(1), Expr::val(2));
5351 let c = Expr::val(true);
5352
5353 let e = Expr::ite(a, b.clone(), c);
5354
5355 let es = Entities::new();
5356
5357 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5358
5359 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5360
5361 assert_eq!(
5362 r,
5363 PartialValue::Residual(Expr::ite(
5364 Expr::unknown(Unknown::new_untyped("guard")),
5365 b,
5366 Expr::val(true)
5367 ))
5368 )
5369 }
5370
5371 #[test]
5372 fn if_semantics_residual_reduce() {
5373 let a = Expr::binary_app(
5374 BinaryOp::Eq,
5375 Expr::get_attr(Expr::var(Var::Context), "condition".into()),
5376 Expr::val("value"),
5377 );
5378 let b = Expr::val("true branch");
5379 let c = Expr::val("false branch");
5380
5381 let e = Expr::ite(a, b.clone(), c.clone());
5382
5383 let es = Entities::new();
5384
5385 let q = Request::new(
5386 (EntityUID::with_eid("p"), None),
5387 (EntityUID::with_eid("a"), None),
5388 (EntityUID::with_eid("r"), None),
5389 Context::from_expr(
5390 RestrictedExpr::new_unchecked(
5391 Expr::record([(
5392 "condition".into(),
5393 Expr::unknown(Unknown::new_untyped("unknown_condition")),
5394 )])
5395 .unwrap(),
5396 )
5397 .as_borrowed(),
5398 Extensions::none(),
5399 )
5400 .unwrap(),
5401 Some(&RequestSchemaAllPass),
5402 Extensions::none(),
5403 )
5404 .unwrap();
5405 let eval = Evaluator::new(q, &es, Extensions::none());
5406
5407 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5408
5409 assert_eq!(
5410 r,
5411 PartialValue::Residual(Expr::ite(
5412 Expr::binary_app(
5413 BinaryOp::Eq,
5414 Expr::unknown(Unknown::new_untyped("unknown_condition")),
5415 Expr::val("value"),
5416 ),
5417 b,
5418 c
5419 ))
5420 );
5421 }
5422
5423 #[test]
5424 fn if_semantics_both_err() {
5425 let a = Expr::unknown(Unknown::new_untyped("guard"));
5426 let b = Expr::and(Expr::val(1), Expr::val(2));
5427 let c = Expr::or(Expr::val(1), Expr::val(3));
5428
5429 let e = Expr::ite(a, b.clone(), c.clone());
5430
5431 let es = Entities::new();
5432
5433 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5434
5435 assert_eq!(
5436 eval.partial_interpret(&e, &HashMap::new()).unwrap(),
5437 PartialValue::Residual(Expr::ite(
5438 Expr::unknown(Unknown::new_untyped("guard")),
5439 b,
5440 c
5441 ))
5442 );
5443 }
5444
5445 #[test]
5446 fn and_semantics1() {
5447 let e = Expr::and(
5449 Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(2)),
5450 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5451 );
5452
5453 let es = Entities::new();
5454 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5455
5456 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5457
5458 assert_eq!(r, PartialValue::Value(Value::from(false)));
5459 }
5460
5461 #[test]
5462 fn and_semantics2() {
5463 let e = Expr::and(
5465 Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2)),
5466 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5467 );
5468
5469 let es = Entities::new();
5470 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5471
5472 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5473
5474 assert_eq!(
5475 r,
5476 PartialValue::Residual(Expr::and(
5477 Expr::val(true),
5478 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false))
5479 ))
5480 );
5481 }
5482
5483 #[test]
5484 fn and_semantics3() {
5485 let e = Expr::and(
5487 Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
5488 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5489 );
5490
5491 let es = Entities::new();
5492 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5493
5494 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5495 }
5496
5497 #[test]
5498 fn and_semantics4() {
5499 let e = Expr::and(
5501 Expr::binary_app(
5502 BinaryOp::Eq,
5503 Expr::unknown(Unknown::new_untyped("a")),
5504 Expr::val(2),
5505 ),
5506 Expr::and(Expr::val("hello"), Expr::val("bye")),
5507 );
5508
5509 let es = Entities::new();
5510 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5511
5512 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Ok(_));
5513 }
5514
5515 #[test]
5516 fn or_semantics1() {
5517 let e = Expr::or(
5520 Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2)),
5521 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5522 );
5523
5524 let es = Entities::new();
5525 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5526
5527 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5528
5529 assert_eq!(r, PartialValue::Value(Value::from(true)));
5530 }
5531
5532 #[test]
5533 fn or_semantics2() {
5534 let e = Expr::or(
5536 Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(2)),
5537 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5538 );
5539
5540 let es = Entities::new();
5541 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5542
5543 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5544
5545 assert_eq!(
5546 r,
5547 PartialValue::Residual(Expr::or(
5548 Expr::val(false),
5549 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false))
5550 ))
5551 );
5552 }
5553
5554 #[test]
5555 fn or_semantics3() {
5556 let e = Expr::or(
5558 Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
5559 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5560 );
5561
5562 let es = Entities::new();
5563 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5564
5565 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5566 }
5567
5568 #[test]
5569 fn or_semantics4() {
5570 let e = Expr::or(
5572 Expr::binary_app(
5573 BinaryOp::Eq,
5574 Expr::unknown(Unknown::new_untyped("a")),
5575 Expr::val(2),
5576 ),
5577 Expr::and(Expr::val("hello"), Expr::val("bye")),
5578 );
5579
5580 let es = Entities::new();
5581 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5582
5583 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Ok(_));
5584 }
5585
5586 #[test]
5587 fn record_semantics_err() {
5588 let a = Expr::get_attr(
5589 Expr::record([("value".into(), Expr::unknown(Unknown::new_untyped("test")))]).unwrap(),
5590 "notpresent".into(),
5591 );
5592
5593 let es = Entities::new();
5594 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5595
5596 assert_matches!(eval.partial_interpret(&a, &HashMap::new()), Err(_));
5597 }
5598
5599 #[test]
5600 fn record_semantics_key_present() {
5601 let a = Expr::get_attr(
5602 Expr::record([("value".into(), Expr::unknown(Unknown::new_untyped("test")))]).unwrap(),
5603 "value".into(),
5604 );
5605
5606 let es = Entities::new();
5607 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5608
5609 let r = eval.partial_interpret(&a, &HashMap::new()).unwrap();
5610
5611 let expected = PartialValue::unknown(Unknown::new_untyped("test"));
5612
5613 assert_eq!(r, expected);
5614 }
5615
5616 #[test]
5617 fn record_semantics_missing_attr() {
5618 let a = Expr::get_attr(
5619 Expr::record([
5620 ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
5621 ("b".into(), Expr::unknown(Unknown::new_untyped("c"))),
5622 ])
5623 .unwrap(),
5624 "c".into(),
5625 );
5626
5627 let es = Entities::new();
5628 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5629
5630 assert_matches!(eval.partial_interpret(&a, &HashMap::new()), Err(_));
5631 }
5632
5633 #[test]
5634 fn record_semantics_mult_unknowns() {
5635 let a = Expr::get_attr(
5636 Expr::record([
5637 ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
5638 ("b".into(), Expr::unknown(Unknown::new_untyped("b"))),
5639 ])
5640 .unwrap(),
5641 "b".into(),
5642 );
5643
5644 let es = Entities::new();
5645 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5646
5647 let r = eval.partial_interpret(&a, &HashMap::new()).unwrap();
5648
5649 let expected = PartialValue::unknown(Unknown::new_untyped("b"));
5650
5651 assert_eq!(r, expected);
5652 }
5653
5654 #[test]
5655 fn partial_if_noerrors() {
5656 let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
5657 let cons = Expr::val(1);
5658 let alt = Expr::val(2);
5659 let e = Expr::ite(guard.clone(), cons, alt);
5660
5661 let es = Entities::new();
5662 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5663
5664 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5665
5666 let expected = Expr::ite(guard, Expr::val(1), Expr::val(2));
5667
5668 assert_eq!(r, PartialValue::Residual(expected));
5669 }
5670
5671 #[test]
5672 fn parital_if_cons_error() {
5673 let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
5674 let cons = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(true));
5675 let alt = Expr::val(2);
5676 let e = Expr::ite(guard.clone(), cons.clone(), alt);
5677
5678 let es = Entities::new();
5679 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5680
5681 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5682
5683 let expected = Expr::ite(guard, cons, Expr::val(2));
5684
5685 assert_eq!(r, PartialValue::Residual(expected));
5686 }
5687
5688 #[test]
5689 fn parital_if_alt_error() {
5690 let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
5691 let cons = Expr::val(2);
5692 let alt = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(true));
5693 let e = Expr::ite(guard.clone(), cons, alt.clone());
5694
5695 let es = Entities::new();
5696 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5697
5698 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5699
5700 let expected = Expr::ite(guard, Expr::val(2), alt);
5701 assert_eq!(r, PartialValue::Residual(expected));
5702 }
5703
5704 #[test]
5705 fn parital_if_both_error() {
5706 let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
5707 let cons = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(true));
5708 let alt = Expr::less(Expr::val("hello"), Expr::val("bye"));
5709 let e = Expr::ite(guard.clone(), cons.clone(), alt.clone());
5710
5711 let es = Entities::new();
5712 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5713
5714 assert_eq!(
5715 eval.partial_interpret(&e, &HashMap::new()).unwrap(),
5716 PartialValue::Residual(Expr::ite(guard, cons, alt))
5717 );
5718 }
5719
5720 #[test]
5722 fn partial_and_err_res() {
5723 let lhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("test"));
5724 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5725 let e = Expr::and(lhs, rhs);
5726 let es = Entities::new();
5727 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5728
5729 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5730 }
5731
5732 #[test]
5734 fn partial_or_err_res() {
5735 let lhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("test"));
5736 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5737 let e = Expr::or(lhs, rhs);
5738 let es = Entities::new();
5739 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5740
5741 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5742 }
5743
5744 #[test]
5746 fn partial_and_true_res() {
5747 let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(1));
5748 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5749 let e = Expr::and(lhs, rhs);
5750 let es = Entities::new();
5751 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5752
5753 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5754
5755 let expected = Expr::and(
5756 Expr::val(true),
5757 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5758 );
5759 assert_eq!(r, PartialValue::Residual(expected));
5760 }
5761
5762 #[test]
5764 fn partial_and_false_res() {
5765 let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5766 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5767 let e = Expr::and(lhs, rhs);
5768 let es = Entities::new();
5769 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5770
5771 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5772 assert_eq!(r, PartialValue::Value(Value::from(false)));
5773 }
5774
5775 #[test]
5777 fn partial_and_res_true() {
5778 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5779 let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2));
5780 let e = Expr::and(lhs.clone(), rhs.clone());
5781 let es = Entities::new();
5782 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5783
5784 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5785 let expected = Expr::and(lhs, rhs);
5786 assert_eq!(r, PartialValue::Residual(expected));
5787 }
5788
5789 #[test]
5790 fn partial_and_res_false() {
5791 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5792 let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5793 let e = Expr::and(lhs.clone(), rhs.clone());
5794 let es = Entities::new();
5795 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5796
5797 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5798 let expected = Expr::and(lhs, rhs);
5799 assert_eq!(r, PartialValue::Residual(expected));
5800 }
5801
5802 #[test]
5804 fn partial_and_res_res() {
5805 let lhs = Expr::unknown(Unknown::new_untyped("b"));
5806 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5807 let e = Expr::and(lhs, rhs);
5808 let es = Entities::new();
5809 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5810
5811 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5812
5813 let expected = Expr::and(
5814 Expr::unknown(Unknown::new_untyped("b")),
5815 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5816 );
5817 assert_eq!(r, PartialValue::Residual(expected));
5818 }
5819
5820 #[test]
5822 fn partial_and_res_err() {
5823 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5824 let rhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("oops"));
5825 let e = Expr::and(lhs, rhs.clone());
5826 let es = Entities::new();
5827 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5828
5829 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5830
5831 let expected = Expr::and(
5832 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5833 rhs,
5834 );
5835 assert_eq!(r, PartialValue::Residual(expected));
5836 }
5837
5838 #[test]
5840 fn partial_or_true_res() {
5841 let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(1));
5842 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5843 let e = Expr::or(lhs, rhs);
5844 let es = Entities::new();
5845 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5846
5847 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5848 assert_eq!(r, PartialValue::Value(Value::from(true)));
5849 }
5850
5851 #[test]
5853 fn partial_or_false_res() {
5854 let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5855 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5856 let e = Expr::or(lhs, rhs);
5857 let es = Entities::new();
5858 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5859
5860 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5861 let expected = Expr::or(
5862 Expr::val(false),
5863 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5864 );
5865 assert_eq!(r, PartialValue::Residual(expected));
5866 }
5867
5868 #[test]
5870 fn partial_or_res_true() {
5871 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5872 let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2));
5873 let e = Expr::or(lhs.clone(), rhs.clone());
5874 let es = Entities::new();
5875 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5876
5877 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5878 let expected = Expr::or(lhs, rhs);
5879 assert_eq!(r, PartialValue::Residual(expected));
5880 }
5881
5882 #[test]
5883 fn partial_or_res_false() {
5884 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5885 let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5886 let e = Expr::or(lhs.clone(), rhs.clone());
5887 let es = Entities::new();
5888 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5889
5890 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5891 let expected = Expr::or(lhs, rhs);
5892 assert_eq!(r, PartialValue::Residual(expected));
5893 }
5894
5895 #[test]
5897 fn partial_or_res_res() {
5898 let lhs = Expr::unknown(Unknown::new_untyped("b"));
5899 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5900 let e = Expr::or(lhs, rhs);
5901 let es = Entities::new();
5902 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5903
5904 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5905
5906 let expected = Expr::or(
5907 Expr::unknown(Unknown::new_untyped("b")),
5908 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5909 );
5910 assert_eq!(r, PartialValue::Residual(expected));
5911 }
5912
5913 #[test]
5915 fn partial_or_res_err() {
5916 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5917 let rhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("oops"));
5918 let e = Expr::or(lhs, rhs.clone());
5919 let es = Entities::new();
5920 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5921
5922 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5923
5924 let expected = Expr::or(
5925 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5926 rhs,
5927 );
5928 assert_eq!(r, PartialValue::Residual(expected));
5929 }
5930
5931 #[test]
5932 fn partial_unop() {
5933 let es = Entities::new();
5934 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5935
5936 let e = Expr::unary_app(UnaryOp::Neg, Expr::unknown(Unknown::new_untyped("a")));
5937 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5938 assert_eq!(r, PartialValue::Residual(e));
5939
5940 let e = Expr::unary_app(UnaryOp::Not, Expr::unknown(Unknown::new_untyped("a")));
5941 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5942 assert_eq!(r, PartialValue::Residual(e));
5943
5944 let e = Expr::unary_app(UnaryOp::IsEmpty, Expr::unknown(Unknown::new_untyped("a")));
5945 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5946 assert_eq!(r, PartialValue::Residual(e));
5947 }
5948
5949 #[test]
5950 fn partial_binop() {
5951 let es = Entities::new();
5952 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5953
5954 let binops = [
5955 BinaryOp::Add,
5956 BinaryOp::Contains,
5957 BinaryOp::ContainsAll,
5958 BinaryOp::ContainsAny,
5959 BinaryOp::Eq,
5960 BinaryOp::In,
5961 BinaryOp::Less,
5962 BinaryOp::LessEq,
5963 BinaryOp::Sub,
5964 ];
5965
5966 for binop in binops {
5967 let e = Expr::binary_app(
5969 binop,
5970 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
5971 Expr::unknown(Unknown::new_untyped("a")),
5972 );
5973 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5974 let expected = Expr::binary_app(
5975 binop,
5976 Expr::val(3),
5977 Expr::unknown(Unknown::new_untyped("a")),
5978 );
5979 assert_eq!(r, PartialValue::Residual(expected));
5980 let e = Expr::binary_app(
5982 binop,
5983 Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
5984 Expr::unknown(Unknown::new_untyped("a")),
5985 );
5986 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5987 let e = Expr::binary_app(
5989 binop,
5990 Expr::unknown(Unknown::new_untyped("a")),
5991 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
5992 );
5993 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5994 let expected = Expr::binary_app(
5995 binop,
5996 Expr::unknown(Unknown::new_untyped("a")),
5997 Expr::val(3),
5998 );
5999 assert_eq!(r, PartialValue::Residual(expected));
6000 let e = Expr::binary_app(
6002 binop,
6003 Expr::unknown(Unknown::new_untyped("a")),
6004 Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
6005 );
6006 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
6007 let e = Expr::binary_app(
6009 binop,
6010 Expr::unknown(Unknown::new_untyped("a")),
6011 Expr::unknown(Unknown::new_untyped("b")),
6012 );
6013 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6014 let expected = Expr::binary_app(
6015 binop,
6016 Expr::unknown(Unknown::new_untyped("a")),
6017 Expr::unknown(Unknown::new_untyped("b")),
6018 );
6019 assert_eq!(r, PartialValue::Residual(expected));
6020 }
6021 }
6022
6023 #[test]
6024 fn partial_mul() {
6025 let es = Entities::new();
6026 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6027
6028 let e = Expr::mul(Expr::unknown(Unknown::new_untyped("a")), Expr::val(32));
6029 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6030 assert_eq!(r, PartialValue::Residual(e));
6031 }
6032
6033 #[test]
6034 fn partial_ext_constructors() {
6035 let es = Entities::new();
6036 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6037
6038 let e = Expr::call_extension_fn(
6039 "ip".parse().unwrap(),
6040 vec![Expr::unknown(Unknown::new_untyped("a"))],
6041 );
6042
6043 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6044
6045 assert_eq!(r, PartialValue::Residual(e));
6046 }
6047
6048 #[cfg(feature = "ipaddr")]
6049 #[test]
6050 fn partial_ext_unfold() {
6051 let es = Entities::new();
6052 let eval = Evaluator::new(empty_request(), &es, Extensions::all_available());
6053
6054 let a = Expr::call_extension_fn("ip".parse().unwrap(), vec![Expr::val("127.0.0.1/32")]);
6055 let b = Expr::unknown(Unknown::new_untyped("a"));
6056 let e = Expr::call_extension_fn("isInRange".parse().unwrap(), vec![a, b]);
6057
6058 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6059
6060 assert_eq!(r, PartialValue::Residual(e));
6061
6062 let b = Expr::call_extension_fn("ip".parse().unwrap(), vec![Expr::val("127.0.0.1/32")]);
6063 let a = Expr::unknown(Unknown::new_untyped("a"));
6064 let e = Expr::call_extension_fn("isInRange".parse().unwrap(), vec![a, b]);
6065
6066 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6067
6068 assert_eq!(r, PartialValue::Residual(e));
6069
6070 let b = Expr::call_extension_fn("ip".parse().unwrap(), vec![Expr::val("invalid")]);
6071 let a = Expr::unknown(Unknown::new_untyped("a"));
6072 let e = Expr::call_extension_fn("isInRange".parse().unwrap(), vec![a, b]);
6073
6074 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
6075 }
6076
6077 #[test]
6078 fn partial_like() {
6079 let es = Entities::new();
6080 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6081
6082 let e = Expr::like(
6083 Expr::unknown(Unknown::new_untyped("a")),
6084 Pattern::from(vec![]),
6085 );
6086
6087 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6088
6089 assert_eq!(r, PartialValue::Residual(e));
6090 }
6091
6092 #[test]
6093 fn partial_is() {
6094 let es = Entities::new();
6095 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6096
6097 let e = Expr::is_entity_type(
6098 Expr::unknown(Unknown::new_untyped("a")),
6099 "User".parse().unwrap(),
6100 );
6101
6102 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6103
6104 assert_eq!(r, PartialValue::Residual(e));
6105 }
6106
6107 #[test]
6108 fn partial_hasattr() {
6109 let es = Entities::new();
6110 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6111
6112 let e = Expr::has_attr(Expr::unknown(Unknown::new_untyped("a")), "test".into());
6113
6114 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6115
6116 assert_eq!(r, PartialValue::Residual(e));
6117 }
6118
6119 #[test]
6120 fn partial_set() {
6121 let es = Entities::new();
6122 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6123
6124 let e = Expr::set([
6125 Expr::val(1),
6126 Expr::unknown(Unknown::new_untyped("a")),
6127 Expr::val(2),
6128 ]);
6129 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6130 assert_eq!(r, PartialValue::Residual(e));
6131
6132 let e = Expr::set([
6133 Expr::val(1),
6134 Expr::unknown(Unknown::new_untyped("a")),
6135 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
6136 ]);
6137 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6138 assert_eq!(
6139 r,
6140 PartialValue::Residual(Expr::set([
6141 Expr::val(1),
6142 Expr::unknown(Unknown::new_untyped("a")),
6143 Expr::val(3)
6144 ]))
6145 );
6146
6147 let e = Expr::set([
6148 Expr::val(1),
6149 Expr::unknown(Unknown::new_untyped("a")),
6150 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("a")),
6151 ]);
6152 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
6153 }
6154
6155 #[test]
6156 fn partial_record() {
6157 let es = Entities::new();
6158 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6159
6160 let e = Expr::record([
6161 ("a".into(), Expr::val(1)),
6162 ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
6163 ("c".into(), Expr::val(2)),
6164 ])
6165 .unwrap();
6166 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6167 assert_eq!(r, PartialValue::Residual(e));
6168
6169 let e = Expr::record([
6170 ("a".into(), Expr::val(1)),
6171 ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
6172 ]);
6173 assert_eq!(
6174 e,
6175 Err(expression_construction_errors::DuplicateKeyError {
6176 key: "a".into(),
6177 context: "in record literal",
6178 }
6179 .into())
6180 );
6181
6182 let e = Expr::record([
6183 ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
6184 ("a".into(), Expr::val(1)),
6185 ]);
6186 assert_eq!(
6187 e,
6188 Err(expression_construction_errors::DuplicateKeyError {
6189 key: "a".into(),
6190 context: "in record literal",
6191 }
6192 .into())
6193 );
6194
6195 let e = Expr::record([
6196 ("a".into(), Expr::val(1)),
6197 ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
6198 (
6199 "c".into(),
6200 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
6201 ),
6202 ])
6203 .unwrap();
6204 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6205 assert_eq!(
6206 r,
6207 PartialValue::Residual(
6208 Expr::record([
6209 ("a".into(), Expr::val(1)),
6210 ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
6211 ("c".into(), Expr::val(3))
6212 ])
6213 .unwrap()
6214 )
6215 );
6216
6217 let e = Expr::record([
6218 ("a".into(), Expr::val(1)),
6219 ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
6220 (
6221 "c".into(),
6222 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("hello")),
6223 ),
6224 ])
6225 .unwrap();
6226 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
6227 }
6228
6229 #[test]
6230 fn small() {
6231 let e = parser::parse_expr("[[1]]").unwrap();
6232 let re = RestrictedExpr::new(e).unwrap();
6233 let eval = RestrictedEvaluator::new(Extensions::none());
6234 let r = eval.partial_interpret(re.as_borrowed()).unwrap();
6235 assert_matches!(r, PartialValue::Value(Value { value: ValueKind::Set(set), .. }) => {
6236 assert_eq!(set.len(), 1);
6237 });
6238 }
6239
6240 #[test]
6241 fn unprojectable_residual() {
6242 let q = basic_request();
6243 let entities = basic_entities();
6244 let eval = Evaluator::new(q, &entities, Extensions::none());
6245
6246 let e = Expr::get_attr(
6247 Expr::record([
6248 (
6249 "a".into(),
6250 Expr::binary_app(
6251 BinaryOp::Add,
6252 Expr::unknown(Unknown::new_untyped("a")),
6253 Expr::val(3),
6254 ),
6255 ),
6256 ("b".into(), Expr::val(83)),
6257 ])
6258 .unwrap(),
6259 "b".into(),
6260 );
6261 let r = eval.partial_eval_expr(&e).unwrap();
6262 assert_eq!(r, Either::Right(e));
6263
6264 let e = Expr::get_attr(
6265 Expr::record([(
6266 "a".into(),
6267 Expr::binary_app(
6268 BinaryOp::Add,
6269 Expr::unknown(Unknown::new_untyped("a")),
6270 Expr::val(3),
6271 ),
6272 )])
6273 .unwrap(),
6274 "b".into(),
6275 );
6276 assert_matches!(eval.partial_eval_expr(&e), Err(_));
6277 }
6278
6279 #[test]
6280 fn interpret_extended_has() {
6281 let es = Entities::new();
6282 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6283 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6284 {a: {b: {c: 1}}} has a.b.c
6285 "#).unwrap()), Ok(v) => {
6286 assert_eq!(v, Value::from(true));
6287 });
6288 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6289 {a: {b: {c: 1}}} has a.b
6290 "#).unwrap()), Ok(v) => {
6291 assert_eq!(v, Value::from(true));
6292 });
6293 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6294 {a: {b: {c: 1}}} has a
6295 "#).unwrap()), Ok(v) => {
6296 assert_eq!(v, Value::from(true));
6297 });
6298 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6299 {a: {b: {c: 1}}} has b.c
6300 "#).unwrap()), Ok(v) => {
6301 assert_eq!(v, Value::from(false));
6302 });
6303 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6304 {a: {b: {c: 1}}} has c
6305 "#).unwrap()), Ok(v) => {
6306 assert_eq!(v, Value::from(false));
6307 });
6308 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6309 {a: {b: {c: 1}}} has d
6310 "#).unwrap()), Ok(v) => {
6311 assert_eq!(v, Value::from(false));
6312 });
6313 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6314 {a: {b: {c: 1}}} has "🚫"
6315 "#).unwrap()), Ok(v) => {
6316 assert_eq!(v, Value::from(false));
6317 });
6318
6319 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6320 {a: {b: {c: 1}}} has a.b.c && {a: {b: {c: 1}}}.a.b.c == 1
6321 "#).unwrap()), Ok(v) => {
6322 assert_eq!(v, Value::from(true));
6323 });
6324 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6325 {a: {b: {c: 1}}} has a.b && {a: {b: {c: 1}}}.a.b == {c: 1}
6326 "#).unwrap()), Ok(v) => {
6327 assert_eq!(v, Value::from(true));
6328 });
6329 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6330 {a: {b: {c: 1}}} has a && {a: {b: {c: 1}}}.a == {b: {c: 1}}
6331 "#).unwrap()), Ok(v) => {
6332 assert_eq!(v, Value::from(true));
6333 });
6334 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6335 {a: {b: {d: 1}}} has a.b.c && {a: {b: {d: 1}}}.a.b.c == 1
6336 "#).unwrap()), Ok(v) => {
6337 assert_eq!(v, Value::from(false));
6338 });
6339
6340 assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6341 {a: {b: {c: 1}}} has a.b && {a: {b: {c: 1}}}.a.b.d == 1
6342 "#).unwrap()), Err(EvaluationError::RecordAttrDoesNotExist(err)) => {
6343 assert_eq!(err.attr, "d");
6344 });
6345 }
6346
6347 #[test]
6348 fn typed_unknown_entity_id() {
6349 let mut q = basic_request();
6350 let entities = basic_entities();
6351 q.principal = EntityUIDEntry::unknown_with_type(
6352 EntityType::from_str("different_test_type").expect("must parse"),
6353 None,
6354 );
6355 q.resource = EntityUIDEntry::unknown_with_type(
6356 EntityType::from_str("other_different_test_type").expect("must parse"),
6357 None,
6358 );
6359 let eval = Evaluator::new(q, &entities, Extensions::none());
6360
6361 let e = Expr::is_entity_type(Expr::var(Var::Principal), EntityUID::test_entity_type());
6362 let r = eval.partial_eval_expr(&e).unwrap();
6363 assert_eq!(r, Either::Left(Value::from(false)));
6364
6365 let e = Expr::is_eq(
6366 Expr::var(Var::Principal),
6367 Expr::val(EntityUID::with_eid("something")),
6368 );
6369 let r = eval.partial_eval_expr(&e).unwrap();
6370 assert_eq!(r, Either::Left(Value::from(false)));
6371
6372 let e = Expr::noteq(
6373 Expr::val(EntityUID::with_eid("something")),
6374 Expr::var(Var::Principal),
6375 );
6376 let r = eval.partial_eval_expr(&e).unwrap();
6377 assert_eq!(r, Either::Left(Value::from(true)));
6378
6379 let e = Expr::is_eq(Expr::var(Var::Principal), Expr::var(Var::Resource));
6381 let r = eval.partial_eval_expr(&e).unwrap();
6382 assert_eq!(r, Either::Left(Value::from(false)));
6383 }
6384}