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