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