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;
34use nonempty::nonempty;
35use smol_str::SmolStr;
36
37#[cfg(not(target_arch = "wasm32"))]
38const REQUIRED_STACK_SPACE: usize = 1024 * 100;
39
40#[allow(clippy::expect_used)]
42mod names {
43 use super::Name;
44 lazy_static::lazy_static! {
45 pub static ref ANY_ENTITY_TYPE : Name = Name::parse_unqualified_name("any_entity_type").expect("valid identifier");
46 }
47}
48
49pub struct Evaluator<'e> {
55 principal: EntityUIDEntry,
57 action: EntityUIDEntry,
59 resource: EntityUIDEntry,
61 context: PartialValue,
63 entities: &'e Entities,
69 extensions: &'e Extensions<'e>,
71}
72
73#[derive(Debug)]
75pub struct RestrictedEvaluator<'e> {
76 extensions: &'e Extensions<'e>,
78}
79
80impl<'e> RestrictedEvaluator<'e> {
81 pub fn new(extensions: &'e Extensions<'e>) -> Self {
83 Self { extensions }
84 }
85
86 pub fn interpret(&self, e: BorrowedRestrictedExpr<'_>) -> Result<Value> {
90 match self.partial_interpret(e)? {
91 PartialValue::Value(v) => Ok(v),
92 PartialValue::Residual(r) => Err(EvaluationError::non_value(r)),
93 }
94 }
95
96 pub fn partial_interpret(&self, expr: BorrowedRestrictedExpr<'_>) -> Result<PartialValue> {
102 stack_size_check()?;
103
104 let res = self.partial_interpret_internal(&expr);
105
106 res.map(|pval| pval.with_maybe_source_loc(expr.source_loc().cloned()))
114 .map_err(|err| match err.source_loc() {
115 None => err.with_maybe_source_loc(expr.source_loc().cloned()),
116 Some(_) => err,
117 })
118 }
119
120 fn partial_interpret_internal(
132 &self,
133 expr: &BorrowedRestrictedExpr<'_>,
134 ) -> Result<PartialValue> {
135 match expr.as_ref().expr_kind() {
136 ExprKind::Lit(lit) => Ok(lit.clone().into()),
137 ExprKind::Set(items) => {
138 let vals = items
139 .iter()
140 .map(|item| self.partial_interpret(BorrowedRestrictedExpr::new_unchecked(item))) .collect::<Result<Vec<_>>>()?;
142 match split(vals) {
143 Either::Left(values) => Ok(Value::set(values, expr.source_loc().cloned()).into()),
144 Either::Right(residuals) => Ok(Expr::set(residuals).into()),
145 }
146 }
147 ExprKind::Unknown(u) => Ok(PartialValue::unknown(u.clone())),
148 ExprKind::Record(map) => {
149 let map = map
150 .iter()
151 .map(|(k, v)| Ok((k.clone(), self.partial_interpret(BorrowedRestrictedExpr::new_unchecked(v))?))) .collect::<Result<Vec<_>>>()?;
153 let (names, attrs) : (Vec<_>, Vec<_>) = map.into_iter().unzip();
154 match split(attrs) {
155 Either::Left(values) => Ok(Value::record(names.into_iter().zip(values), expr.source_loc().cloned()).into()),
156 Either::Right(residuals) => {
157 #[allow(clippy::expect_used)]
159 Ok(
160 Expr::record(names.into_iter().zip(residuals))
161 .expect("can't have a duplicate key here because `names` is the set of keys of the input `BTreeMap`")
162 .into()
163 )
164 }
165 }
166 }
167 ExprKind::ExtensionFunctionApp { fn_name, args } => {
168 let args = args
169 .iter()
170 .map(|arg| self.partial_interpret(BorrowedRestrictedExpr::new_unchecked(arg))) .collect::<Result<Vec<_>>>()?;
172 match split(args) {
173 Either::Left(values) => {
174 let values : Vec<_> = values.collect();
175 let efunc = self.extensions.func(fn_name)?;
176 efunc.call(&values)
177 },
178 Either::Right(residuals) => Ok(Expr::call_extension_fn(fn_name.clone(), residuals.collect()).into()),
179 }
180 },
181 #[allow(clippy::unreachable)]
183 expr => unreachable!("internal invariant violation: BorrowedRestrictedExpr somehow contained this expr case: {expr:?}"),
184 }
185 }
186}
187
188impl<'e> Evaluator<'e> {
189 pub fn new(q: Request, entities: &'e Entities, extensions: &'e Extensions<'e>) -> Self {
193 Self {
194 principal: q.principal,
195 action: q.action,
196 resource: q.resource,
197 context: {
198 match q.context {
199 None => PartialValue::unknown(Unknown::new_untyped("context")),
200 Some(ctx) => ctx.into(),
201 }
202 },
203 entities,
204 extensions,
205 }
206 }
207
208 pub fn evaluate(&self, p: &Policy) -> Result<bool> {
215 self.interpret(&p.condition(), p.env())?.get_as_bool()
216 }
217
218 pub fn partial_evaluate(&self, p: &Policy) -> Result<Either<bool, Expr>> {
228 match self.partial_interpret(&p.condition(), p.env())? {
229 PartialValue::Value(v) => v.get_as_bool().map(Either::Left),
230 PartialValue::Residual(e) => Ok(Either::Right(e)),
231 }
232 }
233
234 pub fn interpret(&self, e: &Expr, slots: &SlotEnv) -> Result<Value> {
240 match self.partial_interpret(e, slots)? {
241 PartialValue::Value(v) => Ok(v),
242 PartialValue::Residual(r) => Err(EvaluationError::non_value(r)),
243 }
244 }
245
246 pub fn partial_interpret(&self, expr: &Expr, slots: &SlotEnv) -> Result<PartialValue> {
252 stack_size_check()?;
253
254 let res = self.partial_interpret_internal(expr, slots);
255
256 res.map(|pval| pval.with_maybe_source_loc(expr.source_loc().cloned()))
264 .map_err(|err| match err.source_loc() {
265 None => err.with_maybe_source_loc(expr.source_loc().cloned()),
266 Some(_) => err,
267 })
268 }
269
270 fn partial_interpret_internal(&self, expr: &Expr, slots: &SlotEnv) -> Result<PartialValue> {
280 let loc = expr.source_loc(); match expr.expr_kind() {
282 ExprKind::Lit(lit) => Ok(lit.clone().into()),
283 ExprKind::Slot(id) => slots
284 .get(id)
285 .ok_or_else(|| err::EvaluationError::unlinked_slot(*id, loc.cloned()))
286 .map(|euid| PartialValue::from(euid.clone())),
287 ExprKind::Var(v) => match v {
288 Var::Principal => Ok(self.principal.evaluate(*v)),
289 Var::Action => Ok(self.action.evaluate(*v)),
290 Var::Resource => Ok(self.resource.evaluate(*v)),
291 Var::Context => Ok(self.context.clone()),
292 },
293 ExprKind::Unknown(_) => Ok(PartialValue::Residual(expr.clone())),
294 ExprKind::If {
295 test_expr,
296 then_expr,
297 else_expr,
298 } => self.eval_if(test_expr, then_expr, else_expr, slots),
299 ExprKind::And { left, right } => {
300 match self.partial_interpret(left, slots)? {
301 PartialValue::Residual(e) => {
303 Ok(PartialValue::Residual(Expr::and(e, right.as_ref().clone())))
304 }
305 PartialValue::Value(v) => {
307 if v.get_as_bool()? {
308 match self.partial_interpret(right, slots)? {
309 PartialValue::Residual(right) => {
313 Ok(PartialValue::Residual(Expr::and(Expr::val(true), right)))
314 }
315 PartialValue::Value(v) => Ok(v.get_as_bool()?.into()),
317 }
318 } else {
319 Ok(false.into())
321 }
322 }
323 }
324 }
325 ExprKind::Or { left, right } => {
326 match self.partial_interpret(left, slots)? {
327 PartialValue::Residual(r) => {
329 Ok(PartialValue::Residual(Expr::or(r, right.as_ref().clone())))
330 }
331 PartialValue::Value(lhs) => {
333 if lhs.get_as_bool()? {
334 Ok(true.into())
336 } else {
337 match self.partial_interpret(right, slots)? {
338 PartialValue::Residual(rhs) =>
339 {
343 Ok(PartialValue::Residual(Expr::or(Expr::val(false), rhs)))
344 }
345 PartialValue::Value(v) => Ok(v.get_as_bool()?.into()),
346 }
347 }
348 }
349 }
350 }
351 ExprKind::UnaryApp { op, arg } => match self.partial_interpret(arg, slots)? {
352 PartialValue::Value(arg) => match op {
353 UnaryOp::Not => match arg.get_as_bool()? {
354 true => Ok(false.into()),
355 false => Ok(true.into()),
356 },
357 UnaryOp::Neg => {
358 let i = arg.get_as_long()?;
359 match i.checked_neg() {
360 Some(v) => Ok(v.into()),
361 None => Err(IntegerOverflowError::UnaryOp(UnaryOpOverflowError {
362 op: *op,
363 arg,
364 source_loc: loc.cloned(),
365 })
366 .into()),
367 }
368 }
369 },
370 PartialValue::Residual(r) => Ok(PartialValue::Residual(Expr::unary_app(*op, r))),
373 },
374 ExprKind::BinaryApp { op, arg1, arg2 } => {
375 let (arg1, arg2) = match (
379 self.partial_interpret(arg1, slots)?,
380 self.partial_interpret(arg2, slots)?,
381 ) {
382 (PartialValue::Value(v1), PartialValue::Value(v2)) => (v1, v2),
383 (PartialValue::Value(v1), PartialValue::Residual(e2)) => {
384 return Ok(PartialValue::Residual(Expr::binary_app(*op, v1.into(), e2)))
385 }
386 (PartialValue::Residual(e1), PartialValue::Value(v2)) => {
387 return Ok(PartialValue::Residual(Expr::binary_app(*op, e1, v2.into())))
388 }
389 (PartialValue::Residual(e1), PartialValue::Residual(e2)) => {
390 return Ok(PartialValue::Residual(Expr::binary_app(*op, e1, e2)))
391 }
392 };
393 match op {
394 BinaryOp::Eq => Ok((arg1 == arg2).into()),
395 BinaryOp::Less
397 | BinaryOp::LessEq
398 | BinaryOp::Add
399 | BinaryOp::Sub
400 | BinaryOp::Mul => {
401 let i1 = arg1.get_as_long()?;
402 let i2 = arg2.get_as_long()?;
403 match op {
404 BinaryOp::Less => Ok((i1 < i2).into()),
405 BinaryOp::LessEq => Ok((i1 <= i2).into()),
406 BinaryOp::Add => match i1.checked_add(i2) {
407 Some(sum) => Ok(sum.into()),
408 None => {
409 Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
410 op: *op,
411 arg1,
412 arg2,
413 source_loc: loc.cloned(),
414 })
415 .into())
416 }
417 },
418 BinaryOp::Sub => match i1.checked_sub(i2) {
419 Some(diff) => Ok(diff.into()),
420 None => {
421 Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
422 op: *op,
423 arg1,
424 arg2,
425 source_loc: loc.cloned(),
426 })
427 .into())
428 }
429 },
430 BinaryOp::Mul => match i1.checked_mul(i2) {
431 Some(prod) => Ok(prod.into()),
432 None => {
433 Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
434 op: *op,
435 arg1,
436 arg2,
437 source_loc: loc.cloned(),
438 })
439 .into())
440 }
441 },
442 #[allow(clippy::unreachable)]
444 _ => {
445 unreachable!("Should have already checked that op was one of these")
446 }
447 }
448 }
449 BinaryOp::In => {
451 let uid1 = arg1.get_as_entity().map_err(|mut e|
452 {
453 if let EvaluationError::TypeError(TypeError { advice, .. }) = &mut e {
457 match arg2.type_of() {
458 Type::Set => *advice = Some("`in` is for checking the entity hierarchy; use `.contains()` to test set membership".into()),
459 Type::Record => *advice = Some("`in` is for checking the entity hierarchy; use `has` to test if a record has a key".into()),
460 _ => {}
461 }
462 };
463 e
464 })?;
465 match self.entities.entity(uid1) {
466 Dereference::Residual(r) => Ok(PartialValue::Residual(
467 Expr::binary_app(BinaryOp::In, r, arg2.into()),
468 )),
469 Dereference::NoSuchEntity => self.eval_in(uid1, None, arg2),
470 Dereference::Data(entity1) => self.eval_in(uid1, Some(entity1), arg2),
471 }
472 }
473 BinaryOp::Contains => match arg1.value {
475 ValueKind::Set(Set { fast: Some(h), .. }) => match arg2.try_as_lit() {
476 Some(lit) => Ok((h.contains(lit)).into()),
477 None => Ok(false.into()), },
479 ValueKind::Set(Set {
480 fast: None,
481 authoritative,
482 }) => Ok((authoritative.contains(&arg2)).into()),
483 _ => Err(EvaluationError::type_error_single(Type::Set, &arg1)),
484 },
485 BinaryOp::ContainsAll | BinaryOp::ContainsAny => {
487 let arg1_set = arg1.get_as_set()?;
488 let arg2_set = arg2.get_as_set()?;
489 match (&arg1_set.fast, &arg2_set.fast) {
490 (Some(arg1_set), Some(arg2_set)) => {
491 match op {
494 BinaryOp::ContainsAll => {
495 Ok((arg2_set.is_subset(arg1_set)).into())
496 }
497 BinaryOp::ContainsAny => {
498 Ok((!arg1_set.is_disjoint(arg2_set)).into())
499 }
500 #[allow(clippy::unreachable)]
502 _ => unreachable!(
503 "Should have already checked that op was one of these"
504 ),
505 }
506 }
507 (_, _) => {
508 match op {
511 BinaryOp::ContainsAll => {
512 let is_subset = arg2_set
513 .authoritative
514 .iter()
515 .all(|item| arg1_set.authoritative.contains(item));
516 Ok(is_subset.into())
517 }
518 BinaryOp::ContainsAny => {
519 let not_disjoint = arg1_set
520 .authoritative
521 .iter()
522 .any(|item| arg2_set.authoritative.contains(item));
523 Ok(not_disjoint.into())
524 }
525 #[allow(clippy::unreachable)]
527 _ => unreachable!(
528 "Should have already checked that op was one of these"
529 ),
530 }
531 }
532 }
533 }
534 }
535 }
536 ExprKind::ExtensionFunctionApp { fn_name, args } => {
537 let args = args
538 .iter()
539 .map(|arg| self.partial_interpret(arg, slots))
540 .collect::<Result<Vec<_>>>()?;
541 match split(args) {
542 Either::Left(vals) => {
543 let vals: Vec<_> = vals.collect();
544 let efunc = self.extensions.func(fn_name)?;
545 efunc.call(&vals)
546 }
547 Either::Right(residuals) => Ok(PartialValue::Residual(
548 Expr::call_extension_fn(fn_name.clone(), residuals.collect()),
549 )),
550 }
551 }
552 ExprKind::GetAttr { expr, attr } => self.get_attr(expr.as_ref(), attr, slots, loc),
553 ExprKind::HasAttr { expr, attr } => match self.partial_interpret(expr, slots)? {
554 PartialValue::Value(Value {
555 value: ValueKind::Record(record),
556 ..
557 }) => Ok(record.get(attr).is_some().into()),
558 PartialValue::Value(Value {
559 value: ValueKind::Lit(Literal::EntityUID(uid)),
560 ..
561 }) => match self.entities.entity(&uid) {
562 Dereference::NoSuchEntity => Ok(false.into()),
563 Dereference::Residual(r) => {
564 Ok(PartialValue::Residual(Expr::has_attr(r, attr.clone())))
565 }
566 Dereference::Data(e) => Ok(e.get(attr).is_some().into()),
567 },
568 PartialValue::Value(val) => Err(err::EvaluationError::type_error(
569 nonempty![
570 Type::Record,
571 Type::entity_type(names::ANY_ENTITY_TYPE.clone())
572 ],
573 &val,
574 )),
575 PartialValue::Residual(r) => Ok(Expr::has_attr(r, attr.clone()).into()),
576 },
577 ExprKind::Like { expr, pattern } => {
578 let v = self.partial_interpret(expr, slots)?;
579 match v {
580 PartialValue::Value(v) => {
581 Ok((pattern.wildcard_match(v.get_as_string()?)).into())
582 }
583 PartialValue::Residual(r) => Ok(Expr::like(r, pattern.iter().cloned()).into()),
584 }
585 }
586 ExprKind::Is { expr, entity_type } => {
587 let v = self.partial_interpret(expr, slots)?;
588 match v {
589 PartialValue::Value(v) => {
590 Ok((v.get_as_entity()?.entity_type() == entity_type).into())
591 }
592 PartialValue::Residual(r) => {
593 Ok(Expr::is_entity_type(r, entity_type.clone()).into())
594 }
595 }
596 }
597 ExprKind::Set(items) => {
598 let vals = items
599 .iter()
600 .map(|item| self.partial_interpret(item, slots))
601 .collect::<Result<Vec<_>>>()?;
602 match split(vals) {
603 Either::Left(vals) => Ok(Value::set(vals, loc.cloned()).into()),
604 Either::Right(r) => Ok(Expr::set(r).into()),
605 }
606 }
607 ExprKind::Record(map) => {
608 let map = map
609 .iter()
610 .map(|(k, v)| Ok((k.clone(), self.partial_interpret(v, slots)?)))
611 .collect::<Result<Vec<_>>>()?;
612 let (names, evalled): (Vec<SmolStr>, Vec<PartialValue>) = map.into_iter().unzip();
613 match split(evalled) {
614 Either::Left(vals) => {
615 Ok(Value::record(names.into_iter().zip(vals), loc.cloned()).into())
616 }
617 Either::Right(rs) => {
618 #[allow(clippy::expect_used)]
620 Ok(
621 Expr::record(names.into_iter().zip(rs))
622 .expect("can't have a duplicate key here because `names` is the set of keys of the input `BTreeMap`")
623 .into()
624 )
625 }
626 }
627 }
628 }
629 }
630
631 fn eval_in(
632 &self,
633 uid1: &EntityUID,
634 entity1: Option<&Entity>,
635 arg2: Value,
636 ) -> Result<PartialValue> {
637 let rhs = match arg2.value {
640 ValueKind::Lit(Literal::EntityUID(uid)) => vec![(*uid).clone()],
641 ValueKind::Set(Set { authoritative, .. }) => authoritative
644 .iter()
645 .map(|val| Ok(val.get_as_entity()?.clone()))
646 .collect::<Result<Vec<EntityUID>>>()?,
647 _ => {
648 return Err(EvaluationError::type_error(
649 nonempty![Type::Set, Type::entity_type(names::ANY_ENTITY_TYPE.clone())],
650 &arg2,
651 ))
652 }
653 };
654 for uid2 in rhs {
655 if uid1 == &uid2
656 || entity1
657 .map(|e1| e1.is_descendant_of(&uid2))
658 .unwrap_or(false)
659 {
660 return Ok(true.into());
661 }
662 }
663 Ok(false.into())
666 }
667
668 fn eval_if(
671 &self,
672 guard: &Expr,
673 consequent: &Arc<Expr>,
674 alternative: &Arc<Expr>,
675 slots: &SlotEnv,
676 ) -> Result<PartialValue> {
677 match self.partial_interpret(guard, slots)? {
678 PartialValue::Value(v) => {
679 if v.get_as_bool()? {
680 self.partial_interpret(consequent, slots)
681 } else {
682 self.partial_interpret(alternative, slots)
683 }
684 }
685 PartialValue::Residual(guard) => {
686 Ok(Expr::ite_arc(Arc::new(guard), consequent.clone(), alternative.clone()).into())
687 }
688 }
689 }
690
691 fn get_attr(
695 &self,
696 expr: &Expr,
697 attr: &SmolStr,
698 slots: &SlotEnv,
699 source_loc: Option<&Loc>,
700 ) -> Result<PartialValue> {
701 match self.partial_interpret(expr, slots)? {
702 PartialValue::Residual(res) => {
704 match res.expr_kind() {
705 ExprKind::Record(map) => {
706 if res.is_projectable() {
711 map.as_ref()
712 .iter()
713 .filter_map(|(k, v)| if k == attr { Some(v) } else { None })
714 .next()
715 .ok_or_else(|| {
716 EvaluationError::record_attr_does_not_exist(
717 attr.clone(),
718 map.keys(),
719 map.len(),
720 source_loc.cloned(),
721 )
722 })
723 .and_then(|e| self.partial_interpret(e, slots))
724 } else if map.keys().any(|k| k == attr) {
725 Ok(PartialValue::Residual(Expr::get_attr(
726 Expr::record_arc(Arc::clone(map)),
727 attr.clone(),
728 )))
729 } else {
730 Err(EvaluationError::record_attr_does_not_exist(
731 attr.clone(),
732 map.keys(),
733 map.len(),
734 source_loc.cloned(),
735 ))
736 }
737 }
738 _ => Ok(PartialValue::Residual(Expr::get_attr(res, attr.clone()))),
740 }
741 }
742 PartialValue::Value(Value {
743 value: ValueKind::Record(record),
744 ..
745 }) => record
746 .as_ref()
747 .get(attr)
748 .ok_or_else(|| {
749 EvaluationError::record_attr_does_not_exist(
750 attr.clone(),
751 record.keys(),
752 record.len(),
753 source_loc.cloned(),
754 )
755 })
756 .map(|v| PartialValue::Value(v.clone())),
757 PartialValue::Value(Value {
758 value: ValueKind::Lit(Literal::EntityUID(uid)),
759 loc,
760 }) => match self.entities.entity(uid.as_ref()) {
761 Dereference::NoSuchEntity => {
762 Err(EvaluationError::entity_does_not_exist(uid.clone(), loc))
764 }
765 Dereference::Residual(r) => {
766 Ok(PartialValue::Residual(Expr::get_attr(r, attr.clone())))
767 }
768 Dereference::Data(entity) => entity
769 .get(attr)
770 .ok_or_else(|| {
771 EvaluationError::entity_attr_does_not_exist(
772 uid,
773 attr.clone(),
774 entity.keys(),
775 entity.attrs_len(),
776 source_loc.cloned(),
777 )
778 })
779 .cloned(),
780 },
781 PartialValue::Value(v) => {
782 #[allow(clippy::unwrap_used)]
784 Err(EvaluationError::type_error(
785 nonempty![
786 Type::Record,
787 Type::entity_type(names::ANY_ENTITY_TYPE.clone()),
788 ],
789 &v,
790 ))
791 }
792 }
793 }
794
795 #[cfg(test)]
796 pub fn interpret_inline_policy(&self, e: &Expr) -> Result<Value> {
797 match self.partial_interpret(e, &HashMap::new())? {
798 PartialValue::Value(v) => {
799 debug_assert!(e.source_loc().is_some() == v.source_loc().is_some());
800 Ok(v)
801 }
802 PartialValue::Residual(r) => {
803 debug_assert!(e.source_loc().is_some() == r.source_loc().is_some());
804 Err(err::EvaluationError::non_value(r))
805 }
806 }
807 }
808
809 #[cfg(test)]
811 pub fn partial_eval_expr(&self, p: &Expr) -> Result<Either<Value, Expr>> {
812 let env = SlotEnv::new();
813 match self.partial_interpret(p, &env)? {
814 PartialValue::Value(v) => Ok(Either::Left(v)),
815 PartialValue::Residual(r) => Ok(Either::Right(r)),
816 }
817 }
818 }
824
825impl<'e> std::fmt::Debug for Evaluator<'e> {
826 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
827 write!(
828 f,
829 "<Evaluator with principal = {:?}, action = {:?}, resource = {:?}",
830 &self.principal, &self.action, &self.resource
831 )
832 }
833}
834
835impl Value {
836 pub(crate) fn get_as_bool(&self) -> Result<bool> {
839 match &self.value {
840 ValueKind::Lit(Literal::Bool(b)) => Ok(*b),
841 _ => Err(EvaluationError::type_error_single(Type::Bool, self)),
842 }
843 }
844
845 pub(crate) fn get_as_long(&self) -> Result<Integer> {
848 match &self.value {
849 ValueKind::Lit(Literal::Long(i)) => Ok(*i),
850 _ => Err(EvaluationError::type_error_single(Type::Long, self)),
851 }
852 }
853
854 pub(crate) fn get_as_string(&self) -> Result<&SmolStr> {
857 match &self.value {
858 ValueKind::Lit(Literal::String(s)) => Ok(s),
859 _ => Err(EvaluationError::type_error_single(Type::String, self)),
860 }
861 }
862
863 pub(crate) fn get_as_set(&self) -> Result<&Set> {
865 match &self.value {
866 ValueKind::Set(set) => Ok(set),
867 _ => Err(EvaluationError::type_error_single(Type::Set, self)),
868 }
869 }
870
871 pub(crate) fn get_as_record(&self) -> Result<&Arc<BTreeMap<SmolStr, Value>>> {
873 match &self.value {
874 ValueKind::Record(rec) => Ok(rec),
875 _ => Err(EvaluationError::type_error_single(Type::Record, self)),
876 }
877 }
878
879 pub(crate) fn get_as_entity(&self) -> Result<&EntityUID> {
882 match &self.value {
883 ValueKind::Lit(Literal::EntityUID(uid)) => Ok(uid.as_ref()),
884 _ => Err(EvaluationError::type_error_single(
885 Type::entity_type(names::ANY_ENTITY_TYPE.clone()),
886 self,
887 )),
888 }
889 }
890}
891
892#[inline(always)]
893fn stack_size_check() -> Result<()> {
894 #[cfg(not(target_arch = "wasm32"))]
895 {
896 if stacker::remaining_stack().unwrap_or(0) < REQUIRED_STACK_SPACE {
897 return Err(EvaluationError::recursion_limit(None));
898 }
899 }
900 Ok(())
901}
902
903#[allow(clippy::panic)]
905#[cfg(test)]
906pub mod test {
907 use std::str::FromStr;
908
909 use super::*;
910
911 use crate::{
912 entities::{EntityJsonParser, NoEntitiesSchema, TCComputation},
913 parser::{self, parse_expr, parse_policy_or_template, parse_policyset},
914 };
915
916 use cool_asserts::assert_matches;
917
918 pub fn basic_request() -> Request {
920 Request::new(
921 (EntityUID::with_eid("test_principal"), None),
922 (EntityUID::with_eid("test_action"), None),
923 (EntityUID::with_eid("test_resource"), None),
924 Context::from_pairs(
925 [
926 ("cur_time".into(), RestrictedExpr::val("03:22:11")),
927 (
928 "device_properties".into(),
929 RestrictedExpr::record(vec![
930 ("os_name".into(), RestrictedExpr::val("Windows")),
931 ("manufacturer".into(), RestrictedExpr::val("ACME Corp")),
932 ])
933 .unwrap(),
934 ),
935 ],
936 Extensions::none(),
937 )
938 .unwrap(),
939 Some(&RequestSchemaAllPass),
940 Extensions::none(),
941 )
942 .unwrap()
943 }
944
945 pub fn basic_entities() -> Entities {
947 Entities::from_entities(
948 vec![
949 Entity::with_uid(EntityUID::with_eid("foo")),
950 Entity::with_uid(EntityUID::with_eid("test_principal")),
951 Entity::with_uid(EntityUID::with_eid("test_action")),
952 Entity::with_uid(EntityUID::with_eid("test_resource")),
953 ],
954 None::<&NoEntitiesSchema>,
955 TCComputation::ComputeNow,
956 Extensions::none(),
957 )
958 .expect("failed to create basic entities")
959 }
960
961 pub fn rich_entities() -> Entities {
963 let entity_no_attrs_no_parents =
964 Entity::with_uid(EntityUID::with_eid("entity_no_attrs_no_parents"));
965 let mut entity_with_attrs = Entity::with_uid(EntityUID::with_eid("entity_with_attrs"));
966 entity_with_attrs
967 .set_attr("spoon".into(), RestrictedExpr::val(787), Extensions::none())
968 .unwrap();
969 entity_with_attrs
970 .set_attr(
971 "tags".into(),
972 RestrictedExpr::set(vec![
973 RestrictedExpr::val("fun"),
974 RestrictedExpr::val("good"),
975 RestrictedExpr::val("useful"),
976 ]),
977 Extensions::none(),
978 )
979 .unwrap();
980 entity_with_attrs
981 .set_attr(
982 "address".into(),
983 RestrictedExpr::record(vec![
984 ("street".into(), RestrictedExpr::val("234 magnolia")),
985 ("town".into(), RestrictedExpr::val("barmstadt")),
986 ("country".into(), RestrictedExpr::val("amazonia")),
987 ])
988 .unwrap(),
989 Extensions::none(),
990 )
991 .unwrap();
992 let mut child = Entity::with_uid(EntityUID::with_eid("child"));
993 let mut parent = Entity::with_uid(EntityUID::with_eid("parent"));
994 let grandparent = Entity::with_uid(EntityUID::with_eid("grandparent"));
995 let mut sibling = Entity::with_uid(EntityUID::with_eid("sibling"));
996 let unrelated = Entity::with_uid(EntityUID::with_eid("unrelated"));
997 child.add_ancestor(parent.uid().clone());
998 sibling.add_ancestor(parent.uid().clone());
999 parent.add_ancestor(grandparent.uid().clone());
1000 let mut child_diff_type = Entity::with_uid(
1001 EntityUID::with_eid_and_type("other_type", "other_child")
1002 .expect("should be a valid identifier"),
1003 );
1004 child_diff_type.add_ancestor(parent.uid().clone());
1005 child_diff_type.add_ancestor(grandparent.uid().clone());
1006 Entities::from_entities(
1007 vec![
1008 entity_no_attrs_no_parents,
1009 entity_with_attrs,
1010 child,
1011 child_diff_type,
1012 parent,
1013 grandparent,
1014 sibling,
1015 unrelated,
1016 ],
1017 None::<&NoEntitiesSchema>,
1018 TCComputation::ComputeNow,
1019 Extensions::all_available(),
1020 )
1021 .expect("Failed to create rich entities")
1022 }
1023
1024 #[cfg(feature = "partial-eval")]
1025 #[test]
1026 fn partial_entity_stores_in_set() {
1027 let q = basic_request();
1028 let entities = rich_entities().partial();
1029 let child = EntityUID::with_eid("child");
1030 let second = EntityUID::with_eid("joseph");
1031 let missing = EntityUID::with_eid("non-present");
1032 let parent = EntityUID::with_eid("parent");
1033 let eval = Evaluator::new(q, &entities, Extensions::none());
1034
1035 let e = Expr::binary_app(
1036 BinaryOp::In,
1037 Expr::val(child),
1038 Expr::set([Expr::val(parent.clone()), Expr::val(second.clone())]),
1039 );
1040 let r = eval.partial_eval_expr(&e).unwrap();
1041 assert_eq!(r, Either::Left(true.into()));
1042
1043 let e = Expr::binary_app(
1044 BinaryOp::In,
1045 Expr::val(missing.clone()),
1046 Expr::set([Expr::val(parent.clone()), Expr::val(second.clone())]),
1047 );
1048 let r = eval.partial_eval_expr(&e).unwrap();
1049 let expected_residual = Expr::binary_app(
1050 BinaryOp::In,
1051 Expr::unknown(Unknown::new_with_type(
1052 format!("{missing}"),
1053 Type::Entity {
1054 ty: EntityUID::test_entity_type(),
1055 },
1056 )),
1057 Expr::set([Expr::val(parent.clone()), Expr::val(second.clone())]),
1058 );
1059 let expected_residual2 = Expr::binary_app(
1060 BinaryOp::In,
1061 Expr::unknown(Unknown::new_with_type(
1062 format!("{missing}"),
1063 Type::Entity {
1064 ty: EntityUID::test_entity_type(),
1065 },
1066 )),
1067 Expr::set([Expr::val(second), Expr::val(parent)]),
1068 );
1069
1070 assert!(r == Either::Right(expected_residual) || r == Either::Right(expected_residual2));
1072 }
1073
1074 #[cfg(feature = "partial-eval")]
1075 #[test]
1076 fn partial_entity_stores_in() {
1077 let q = basic_request();
1078 let entities = rich_entities().partial();
1079 let child = EntityUID::with_eid("child");
1080 let missing = EntityUID::with_eid("non-present");
1081 let parent = EntityUID::with_eid("parent");
1082 let eval = Evaluator::new(q, &entities, Extensions::none());
1083
1084 let e = Expr::binary_app(BinaryOp::In, Expr::val(child), Expr::val(parent.clone()));
1085 let r = eval.partial_eval_expr(&e).unwrap();
1086 assert_eq!(r, Either::Left(true.into()));
1087
1088 let e = Expr::binary_app(
1089 BinaryOp::In,
1090 Expr::val(missing.clone()),
1091 Expr::val(parent.clone()),
1092 );
1093 let r = eval.partial_eval_expr(&e).unwrap();
1094 let expected_residual = Expr::binary_app(
1095 BinaryOp::In,
1096 Expr::unknown(Unknown::new_with_type(
1097 format!("{missing}"),
1098 Type::Entity {
1099 ty: EntityUID::test_entity_type(),
1100 },
1101 )),
1102 Expr::val(parent),
1103 );
1104 assert_eq!(r, Either::Right(expected_residual));
1105 }
1106
1107 #[cfg(feature = "partial-eval")]
1108 #[test]
1109 fn partial_entity_stores_hasattr() {
1110 let q = basic_request();
1111 let entities = rich_entities().partial();
1112 let has_attr = EntityUID::with_eid("entity_with_attrs");
1113 let missing = EntityUID::with_eid("missing");
1114 let eval = Evaluator::new(q, &entities, Extensions::none());
1115
1116 let e = Expr::has_attr(Expr::val(has_attr), "spoon".into());
1117 let r = eval.partial_eval_expr(&e).unwrap();
1118 assert_eq!(r, Either::Left(true.into()));
1119
1120 let e = Expr::has_attr(Expr::val(missing.clone()), "spoon".into());
1121 let r = eval.partial_eval_expr(&e).unwrap();
1122 let expected_residual = Expr::has_attr(
1123 Expr::unknown(Unknown::new_with_type(
1124 format!("{missing}"),
1125 Type::Entity {
1126 ty: EntityUID::test_entity_type(),
1127 },
1128 )),
1129 "spoon".into(),
1130 );
1131 assert_eq!(r, Either::Right(expected_residual));
1132 }
1133
1134 #[cfg(feature = "partial-eval")]
1135 #[test]
1136 fn partial_entity_stores_getattr() {
1137 let q = basic_request();
1138 let entities = rich_entities().partial();
1139 let has_attr = EntityUID::with_eid("entity_with_attrs");
1140 let missing = EntityUID::with_eid("missing");
1141 let eval = Evaluator::new(q, &entities, Extensions::none());
1142
1143 let e = Expr::get_attr(Expr::val(has_attr), "spoon".into());
1144 let r = eval.partial_eval_expr(&e).unwrap();
1145 assert_eq!(r, Either::Left(787.into()));
1146
1147 let e = Expr::get_attr(Expr::val(missing.clone()), "spoon".into());
1148 let r = eval.partial_eval_expr(&e).unwrap();
1149 let expected_residual = Expr::get_attr(
1150 Expr::unknown(Unknown::new_with_type(
1151 format!("{missing}"),
1152 Type::Entity {
1153 ty: EntityUID::test_entity_type(),
1154 },
1155 )),
1156 "spoon".into(),
1157 );
1158 assert_eq!(r, Either::Right(expected_residual));
1159 }
1160
1161 #[test]
1162 fn interpret_primitives() {
1163 let request = basic_request();
1164 let entities = basic_entities();
1165 let eval = Evaluator::new(request, &entities, Extensions::none());
1166 assert_eq!(
1172 eval.interpret_inline_policy(&Expr::val(false)),
1173 Ok(Value {
1174 value: ValueKind::Lit(Literal::Bool(false)),
1175 loc: None,
1176 }),
1177 );
1178 assert_eq!(
1179 eval.interpret_inline_policy(&Expr::val(true)),
1180 Ok(Value {
1181 value: ValueKind::Lit(Literal::Bool(true)),
1182 loc: None,
1183 }),
1184 );
1185 assert_eq!(
1186 eval.interpret_inline_policy(&Expr::val(57)),
1187 Ok(Value {
1188 value: ValueKind::Lit(Literal::Long(57)),
1189 loc: None,
1190 }),
1191 );
1192 assert_eq!(
1193 eval.interpret_inline_policy(&Expr::val(-3)),
1194 Ok(Value {
1195 value: ValueKind::Lit(Literal::Long(-3)),
1196 loc: None,
1197 }),
1198 );
1199 assert_eq!(
1200 eval.interpret_inline_policy(&Expr::val("")),
1201 Ok(Value {
1202 value: ValueKind::Lit(Literal::String("".into())),
1203 loc: None,
1204 }),
1205 );
1206 assert_eq!(
1207 eval.interpret_inline_policy(&Expr::val("Hello")),
1208 Ok(Value {
1209 value: ValueKind::Lit(Literal::String("Hello".into())),
1210 loc: None,
1211 }),
1212 );
1213 }
1214
1215 #[test]
1216 fn interpret_entities() {
1217 let request = basic_request();
1218 let entities = basic_entities();
1219 let eval = Evaluator::new(request, &entities, Extensions::none());
1220 assert_eq!(
1226 eval.interpret_inline_policy(&Expr::val(EntityUID::with_eid("foo"))),
1227 Ok(Value {
1228 value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid("foo")))),
1229 loc: None,
1230 }),
1231 );
1232 assert_eq!(
1235 eval.interpret_inline_policy(&Expr::val(EntityUID::with_eid("doesnotexist"))),
1236 Ok(Value {
1237 value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid(
1238 "doesnotexist"
1239 )))),
1240 loc: None,
1241 }),
1242 );
1243 }
1244
1245 #[test]
1246 fn interpret_builtin_vars() {
1247 let request = basic_request();
1248 let entities = basic_entities();
1249 let eval = Evaluator::new(request, &entities, Extensions::none());
1250 assert_eq!(
1251 eval.interpret_inline_policy(&Expr::var(Var::Principal)),
1252 Ok(Value::from(EntityUID::with_eid("test_principal")))
1253 );
1254 assert_eq!(
1255 eval.interpret_inline_policy(&Expr::var(Var::Action)),
1256 Ok(Value::from(EntityUID::with_eid("test_action")))
1257 );
1258 assert_eq!(
1259 eval.interpret_inline_policy(&Expr::var(Var::Resource)),
1260 Ok(Value::from(EntityUID::with_eid("test_resource")))
1261 );
1262 }
1263
1264 #[test]
1265 fn interpret_entity_attrs() {
1266 let request = basic_request();
1267 let entities = rich_entities();
1268 let eval = Evaluator::new(request, &entities, Extensions::none());
1269 assert_eq!(
1271 eval.interpret_inline_policy(&Expr::has_attr(
1272 Expr::val(EntityUID::with_eid("entity_no_attrs_no_parents")),
1273 "doesnotexist".into()
1274 )),
1275 Ok(Value::from(false))
1276 );
1277 assert_eq!(
1279 eval.interpret_inline_policy(&Expr::has_attr(
1280 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1281 "doesnotexist".into()
1282 )),
1283 Ok(Value::from(false))
1284 );
1285 assert_eq!(
1287 eval.interpret_inline_policy(&Expr::has_attr(
1288 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1289 "tags".into()
1290 )),
1291 Ok(Value::from(true))
1292 );
1293 assert_matches!(
1295 eval.interpret_inline_policy(&Expr::get_attr(
1296 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1297 "doesnotexist".into()
1298 )),
1299 Err(EvaluationError::EntityAttrDoesNotExist(e)) => {
1300 assert_eq!(e.entity.as_ref(), &EntityUID::with_eid("entity_with_attrs"));
1301 assert_eq!(&e.attr, "doesnotexist");
1302 let available_attrs = e.available_attrs;
1303 assert_eq!(available_attrs.len(), 3);
1304 assert!(available_attrs.contains(&"spoon".into()));
1305 assert!(available_attrs.contains(&"address".into()));
1306 assert!(available_attrs.contains(&"tags".into()));
1307 }
1308 );
1309 assert_eq!(
1311 eval.interpret_inline_policy(&Expr::get_attr(
1312 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1313 "spoon".into()
1314 )),
1315 Ok(Value::from(787))
1316 );
1317 assert_eq!(
1319 eval.interpret_inline_policy(&Expr::contains(
1320 Expr::get_attr(
1321 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1322 "tags".into()
1323 ),
1324 Expr::val("useful")
1325 )),
1326 Ok(Value::from(true))
1327 );
1328 assert_eq!(
1330 eval.interpret_inline_policy(&Expr::has_attr(
1331 Expr::val(EntityUID::with_eid("doesnotexist")),
1332 "foo".into()
1333 )),
1334 Ok(Value::from(false))
1335 );
1336 assert_eq!(
1338 eval.interpret_inline_policy(&Expr::get_attr(
1339 Expr::val(EntityUID::with_eid("doesnotexist")),
1340 "foo".into()
1341 )),
1342 Err(EvaluationError::entity_does_not_exist(
1343 Arc::new(EntityUID::with_eid("doesnotexist")),
1344 None
1345 ))
1346 );
1347 }
1348
1349 #[test]
1350 fn interpret_ternaries() {
1351 let request = basic_request();
1352 let entities = basic_entities();
1353 let eval = Evaluator::new(request, &entities, Extensions::none());
1354 assert_eq!(
1356 eval.interpret_inline_policy(&Expr::ite(Expr::val(true), Expr::val(3), Expr::val(8))),
1357 Ok(Value::from(3))
1358 );
1359 assert_eq!(
1361 eval.interpret_inline_policy(&Expr::ite(Expr::val(false), Expr::val(3), Expr::val(8))),
1362 Ok(Value::from(8))
1363 );
1364 assert_eq!(
1366 eval.interpret_inline_policy(&Expr::ite(
1367 Expr::val(false),
1368 Expr::val(false),
1369 Expr::val(true)
1370 )),
1371 Ok(Value::from(true))
1372 );
1373 assert_eq!(
1375 eval.interpret_inline_policy(&Expr::ite(
1376 Expr::val(false),
1377 Expr::var(Var::Principal),
1378 Expr::var(Var::Resource)
1379 )),
1380 Ok(Value::from(EntityUID::with_eid("test_resource")))
1381 );
1382 assert_matches!(
1384 eval.interpret_inline_policy(&Expr::ite(
1385 Expr::val("hello"),
1386 Expr::val(3),
1387 Expr::val(8)
1388 )),
1389 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1390 assert_eq!(expected, nonempty![Type::Bool]);
1391 assert_eq!(actual, Type::String);
1392 assert_eq!(advice, None);
1393 }
1394 );
1395 assert_matches!(
1397 eval.interpret_inline_policy(&Expr::ite(
1398 Expr::var(Var::Principal),
1399 Expr::val(3),
1400 Expr::val(8)
1401 )),
1402 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1403 assert_eq!(expected, nonempty![Type::Bool]);
1404 assert_eq!(actual, Type::Entity {
1405 ty: EntityUID::test_entity_type(),
1406 });
1407 assert_eq!(advice, None);
1408 }
1409 );
1410 assert_eq!(
1412 eval.interpret_inline_policy(&Expr::ite(
1413 Expr::val(true),
1414 Expr::val("hello"),
1415 Expr::val(2)
1416 )),
1417 Ok(Value::from("hello"))
1418 );
1419 assert_eq!(
1421 eval.interpret_inline_policy(&Expr::ite(
1422 Expr::val(false),
1423 Expr::val("hello"),
1424 Expr::val(2)
1425 )),
1426 Ok(Value::from(2))
1427 );
1428 assert_eq!(
1430 eval.interpret_inline_policy(&Expr::ite(
1431 Expr::val(true),
1432 Expr::ite(Expr::val(true), Expr::val(3), Expr::val(8)),
1433 Expr::val(-10)
1434 )),
1435 Ok(Value::from(3))
1436 );
1437 assert_eq!(
1439 eval.interpret_inline_policy(&Expr::ite(
1440 Expr::val(true),
1441 Expr::ite(Expr::val(false), Expr::val(3), Expr::val(8)),
1442 Expr::val(-10)
1443 )),
1444 Ok(Value::from(8))
1445 );
1446 assert_eq!(
1448 eval.interpret_inline_policy(&Expr::ite(
1449 Expr::val(false),
1450 Expr::ite(Expr::val(false), Expr::val(3), Expr::val(8)),
1451 Expr::val(-10)
1452 )),
1453 Ok(Value::from(-10))
1454 );
1455 assert_eq!(
1457 eval.interpret_inline_policy(&Expr::ite(
1458 Expr::val(false),
1459 Expr::ite(Expr::val("hello"), Expr::val(3), Expr::val(8)),
1460 Expr::val(-10)
1461 )),
1462 Ok(Value::from(-10))
1463 );
1464 assert_eq!(
1466 eval.interpret_inline_policy(&Expr::ite(
1467 Expr::val(true),
1468 Expr::val(3),
1469 Expr::ite(Expr::val(true), Expr::val(8), Expr::val(-10))
1470 )),
1471 Ok(Value::from(3))
1472 );
1473 assert_eq!(
1475 eval.interpret_inline_policy(&Expr::ite(
1476 Expr::ite(Expr::val(true), Expr::val(false), Expr::val(true)),
1477 Expr::val(3),
1478 Expr::val(8)
1479 )),
1480 Ok(Value::from(8))
1481 );
1482 assert_eq!(
1484 eval.interpret_inline_policy(&Expr::ite(
1485 Expr::val(true),
1486 Expr::val(3),
1487 Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
1488 )),
1489 Ok(Value::from(3))
1490 );
1491 assert_eq!(
1493 eval.interpret_inline_policy(&Expr::ite(
1494 Expr::val(false),
1495 Expr::val(3),
1496 Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
1497 )),
1498 Err(EvaluationError::record_attr_does_not_exist(
1499 "foo".into(),
1500 std::iter::empty(),
1501 0,
1502 None,
1503 ))
1504 );
1505 assert_eq!(
1507 eval.interpret_inline_policy(&Expr::ite(
1508 Expr::val(true),
1509 Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
1510 Expr::val(3),
1511 )),
1512 Err(EvaluationError::record_attr_does_not_exist(
1513 "foo".into(),
1514 std::iter::empty(),
1515 0,
1516 None,
1517 ))
1518 );
1519 assert_eq!(
1521 eval.interpret_inline_policy(&Expr::ite(
1522 Expr::val(false),
1523 Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
1524 Expr::val(3),
1525 )),
1526 Ok(Value::from(3))
1527 );
1528 }
1529
1530 #[test]
1531 fn interpret_sets() {
1532 let request = basic_request();
1533 let entities = basic_entities();
1534 let eval = Evaluator::new(request, &entities, Extensions::none());
1535 assert_eq!(
1543 eval.interpret_inline_policy(&Expr::set(vec![Expr::val(8)])),
1544 Ok(Value::set(
1545 vec![Value {
1546 value: ValueKind::Lit(Literal::Long(8)),
1547 loc: None,
1548 }],
1549 None,
1550 )),
1551 );
1552 assert_eq!(
1554 eval.interpret_inline_policy(&Expr::set(vec![
1555 Expr::val(8),
1556 Expr::val(2),
1557 Expr::val(101),
1558 ])),
1559 Ok(Value::set(
1560 vec![
1561 Value {
1562 value: ValueKind::Lit(Literal::Long(8)),
1563 loc: None,
1564 },
1565 Value {
1566 value: ValueKind::Lit(Literal::Long(2)),
1567 loc: None,
1568 },
1569 Value {
1570 value: ValueKind::Lit(Literal::Long(101)),
1571 loc: None,
1572 },
1573 ],
1574 None,
1575 )),
1576 );
1577 assert_eq!(
1579 eval.interpret_inline_policy(&Expr::set(vec![])),
1580 Ok(Value::empty_set(None)),
1581 );
1582 assert_eq!(
1583 eval.interpret_inline_policy(&Expr::set(vec![])),
1584 Ok(Value::empty_set(None)),
1585 );
1586 assert_matches!(
1588 eval.interpret_inline_policy(&Expr::get_attr(
1589 Expr::set(vec![Expr::val(8)]),
1590 "hello".into()
1591 )),
1592 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1593 assert_eq!(expected, nonempty![
1594 Type::Record,
1595 Type::entity_type(
1596 Name::parse_unqualified_name("any_entity_type")
1597 .expect("should be a valid identifier")
1598 ),
1599 ]);
1600 assert_eq!(actual, Type::Set);
1601 assert_eq!(advice, None);
1602 }
1603 );
1604 assert_matches!(
1606 eval.interpret_inline_policy(&Expr::get_attr(Expr::set(vec![]), "hello".into())),
1607 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1608 assert_eq!(expected, nonempty![
1609 Type::Record,
1610 Type::entity_type(
1611 Name::parse_unqualified_name("any_entity_type")
1612 .expect("should be a valid identifier")
1613 ),
1614 ]);
1615 assert_eq!(actual, Type::Set);
1616 assert_eq!(advice, None);
1617 }
1618 );
1619 let mixed_set = Expr::set(vec![
1621 Expr::val("hello"),
1622 Expr::val(2),
1623 Expr::val(true),
1624 Expr::val(EntityUID::with_eid("foo")),
1625 ]);
1626 assert_eq!(
1627 eval.interpret_inline_policy(&mixed_set),
1628 Ok(Value::set(
1629 vec![
1630 Value {
1631 value: ValueKind::Lit(Literal::String("hello".into())),
1632 loc: None,
1633 },
1634 Value {
1635 value: ValueKind::Lit(Literal::Long(2)),
1636 loc: None,
1637 },
1638 Value {
1639 value: ValueKind::Lit(Literal::Bool(true)),
1640 loc: None,
1641 },
1642 Value {
1643 value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid(
1644 "foo"
1645 )))),
1646 loc: None,
1647 },
1648 ],
1649 None,
1650 )),
1651 );
1652 assert_matches!(
1654 eval.interpret_inline_policy(&Expr::get_attr(mixed_set, "hello".into())),
1655 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1656 assert_eq!(expected, nonempty![
1657 Type::Record,
1658 Type::entity_type(
1659 Name::parse_unqualified_name("any_entity_type")
1660 .expect("should be a valid identifier")
1661 ),
1662 ]);
1663 assert_eq!(actual, Type::Set);
1664 assert_eq!(advice, None);
1665 }
1666 );
1667 let set_of_sets = Expr::set(vec![
1669 Expr::set(vec![Expr::val(8), Expr::val(2)]),
1670 Expr::set(vec![Expr::val(13), Expr::val(702)]),
1671 Expr::set(vec![Expr::val(3)]),
1672 ]);
1673 assert_eq!(
1674 eval.interpret_inline_policy(&set_of_sets),
1675 Ok(Value::set(
1676 vec![
1677 Value::set(
1678 vec![
1679 Value {
1680 value: ValueKind::Lit(Literal::Long(8)),
1681 loc: None,
1682 },
1683 Value {
1684 value: ValueKind::Lit(Literal::Long(2)),
1685 loc: None,
1686 },
1687 ],
1688 None,
1689 ),
1690 Value::set(
1691 vec![
1692 Value {
1693 value: ValueKind::Lit(Literal::Long(13)),
1694 loc: None,
1695 },
1696 Value {
1697 value: ValueKind::Lit(Literal::Long(702)),
1698 loc: None,
1699 },
1700 ],
1701 None,
1702 ),
1703 Value::set(
1704 vec![Value {
1705 value: ValueKind::Lit(Literal::Long(3)),
1706 loc: None,
1707 }],
1708 None,
1709 ),
1710 ],
1711 None,
1712 )),
1713 );
1714 assert_matches!(
1716 eval.interpret_inline_policy(&Expr::get_attr(set_of_sets.clone(), "hello".into())),
1717 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1718 assert_eq!(expected, nonempty![
1719 Type::Record,
1720 Type::entity_type(
1721 Name::parse_unqualified_name("any_entity_type")
1722 .expect("should be a valid identifier")
1723 ),
1724 ]);
1725 assert_eq!(actual, Type::Set);
1726 assert_eq!(advice, None);
1727 }
1728 );
1729 assert_matches!(
1731 eval.interpret_inline_policy(&Expr::get_attr(
1732 Expr::get_attr(set_of_sets, "ham".into()),
1733 "eggs".into()
1734 )),
1735 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1736 assert_eq!(expected, nonempty![
1737 Type::Record,
1738 Type::entity_type(
1739 Name::parse_unqualified_name("any_entity_type")
1740 .expect("should be a valid identifier")
1741 ),
1742 ]);
1743 assert_eq!(actual, Type::Set);
1744 assert_eq!(advice, None);
1745 }
1746 );
1747 }
1748
1749 #[test]
1750 fn interpret_records() {
1751 let request = basic_request();
1752 let entities = rich_entities();
1753 let eval = Evaluator::new(request, &entities, Extensions::none());
1754 let string_key = Expr::record(vec![("key".into(), Expr::val(3))]).unwrap();
1756 assert_eq!(
1757 eval.interpret_inline_policy(&Expr::get_attr(string_key, "key".into())),
1758 Ok(Value::from(3))
1759 );
1760 let ham_and_eggs = Expr::record(vec![
1762 ("ham".into(), Expr::val(3)),
1763 ("eggs".into(), Expr::val(7)),
1764 ])
1765 .unwrap();
1766 assert_eq!(
1767 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs.clone(), "ham".into())),
1768 Ok(Value::from(3))
1769 );
1770 assert_eq!(
1772 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs.clone(), "eggs".into())),
1773 Ok(Value::from(7))
1774 );
1775 assert_eq!(
1777 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs, "what".into())),
1778 Err(EvaluationError::record_attr_does_not_exist(
1779 "what".into(),
1780 [&"eggs".into(), &"ham".into()],
1781 2,
1782 None,
1783 ))
1784 );
1785
1786 let ham_and_eggs_2 = Expr::record(vec![
1788 ("ham".into(), Expr::val(3)),
1789 ("eggs".into(), Expr::val("why")),
1790 ])
1791 .unwrap();
1792 assert_eq!(
1793 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs_2.clone(), "ham".into())),
1794 Ok(Value::from(3))
1795 );
1796 assert_eq!(
1798 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs_2, "eggs".into())),
1799 Ok(Value::from("why"))
1800 );
1801 let ham_and_eggs_3 = Expr::record(vec![
1803 ("ham".into(), Expr::val(3)),
1804 ("eggs".into(), Expr::val("why")),
1805 ("else".into(), Expr::val(EntityUID::with_eid("foo"))),
1806 ])
1807 .unwrap();
1808 assert_eq!(
1809 eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs_3, "else".into())),
1810 Ok(Value::from(EntityUID::with_eid("foo")))
1811 );
1812 let hams_and_eggs = Expr::record(vec![
1814 (
1815 "hams".into(),
1816 Expr::record(vec![
1817 ("some".into(), Expr::val(1)),
1818 ("more".into(), Expr::val(2)),
1819 ])
1820 .unwrap(),
1821 ),
1822 ("eggs".into(), Expr::val("why")),
1823 ])
1824 .unwrap();
1825 assert_eq!(
1826 eval.interpret_inline_policy(&Expr::get_attr(
1827 Expr::get_attr(hams_and_eggs, "hams".into()),
1828 "more".into()
1829 )),
1830 Ok(Value::from(2))
1831 );
1832 let weird_key = Expr::record(vec![(
1834 "this is a valid map key+.-_%() ".into(),
1835 Expr::val(7),
1836 )])
1837 .unwrap();
1838 assert_eq!(
1839 eval.interpret_inline_policy(&Expr::get_attr(
1840 weird_key,
1841 "this is a valid map key+.-_%() ".into()
1842 )),
1843 Ok(Value::from(7))
1844 );
1845 assert_eq!(
1847 eval.interpret_inline_policy(&Expr::get_attr(
1848 Expr::record(vec![
1849 ("foo".into(), Expr::val(2)),
1850 (
1851 "bar".into(),
1852 Expr::set(vec!(Expr::val(3), Expr::val(33), Expr::val(333)))
1853 )
1854 ])
1855 .unwrap(),
1856 "bar".into()
1857 )),
1858 Ok(Value::set(
1859 vec![Value::from(3), Value::from(33), Value::from(333)],
1860 None
1861 ))
1862 );
1863 assert_eq!(
1865 eval.interpret_inline_policy(&Expr::get_attr(
1866 Expr::get_attr(
1867 Expr::record(vec![
1868 ("foo".into(), Expr::val(2)),
1869 (
1870 "bar".into(),
1871 Expr::record(vec![
1872 ("a+b".into(), Expr::val(5)),
1873 ("jkl;".into(), Expr::val(10)),
1874 ])
1875 .unwrap()
1876 ),
1877 ])
1878 .unwrap(),
1879 "bar".into()
1880 ),
1881 "a+b".into()
1882 )),
1883 Ok(Value::from(5))
1884 );
1885 assert_eq!(
1887 eval.interpret_inline_policy(&Expr::get_attr(
1888 Expr::get_attr(
1889 Expr::record(vec![
1890 ("foo".into(), Expr::val(2)),
1891 (
1892 "bar".into(),
1893 Expr::record(vec![
1894 ("foo".into(), Expr::val(4)),
1895 ("cake".into(), Expr::val(77)),
1896 ])
1897 .unwrap()
1898 ),
1899 ])
1900 .unwrap(),
1901 "bar".into(),
1902 ),
1903 "foo".into(),
1904 )),
1905 Ok(Value::from(4))
1906 );
1907 assert_eq!(
1910 Expr::record(vec![
1911 ("foo".into(), Expr::val(2)),
1912 ("bar".into(), Expr::val(4)),
1913 ("foo".into(), Expr::val("hi")),
1914 ]),
1915 Err(expression_construction_errors::DuplicateKeyError {
1916 key: "foo".into(),
1917 context: "in record literal",
1918 }
1919 .into())
1920 );
1921 assert_eq!(
1923 eval.interpret_inline_policy(&Expr::get_attr(
1924 Expr::get_attr(
1925 Expr::val(EntityUID::with_eid("entity_with_attrs")),
1926 "address".into()
1927 ),
1928 "street".into()
1929 )),
1930 Ok(Value::from("234 magnolia"))
1931 );
1932 assert_eq!(
1934 eval.interpret_inline_policy(&Expr::get_attr(
1935 Expr::var(Var::Context),
1936 "cur_time".into()
1937 )),
1938 Ok(Value::from("03:22:11"))
1939 );
1940 assert_eq!(
1942 eval.interpret_inline_policy(&Expr::get_attr(
1943 Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
1944 "os_name".into()
1945 )),
1946 Ok(Value::from("Windows"))
1947 );
1948 assert_eq!(
1951 eval.interpret_inline_policy(&Expr::has_attr(
1952 Expr::record(vec![
1953 ("foo".into(), Expr::val(77)),
1954 ("bar".into(), Expr::val("pancakes")),
1955 ])
1956 .unwrap(),
1957 "foo".into()
1958 )),
1959 Ok(Value::from(true))
1960 );
1961 assert_eq!(
1964 eval.interpret_inline_policy(&Expr::has_attr(
1965 Expr::record(vec![
1966 ("foo".into(), Expr::val(77)),
1967 ("bar".into(), Expr::val("pancakes")),
1968 ])
1969 .unwrap(),
1970 "pancakes".into()
1971 )),
1972 Ok(Value::from(false))
1973 );
1974 assert_eq!(
1976 eval.interpret_inline_policy(&Expr::has_attr(
1977 Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap(),
1978 "2".into()
1979 )),
1980 Ok(Value::from(true))
1981 );
1982 assert_eq!(
1984 eval.interpret_inline_policy(&Expr::has_attr(
1985 Expr::record(vec![
1986 ("ham".into(), Expr::val(17)),
1987 (
1988 "eggs".into(),
1989 Expr::ite(
1990 Expr::has_attr(
1991 Expr::val(EntityUID::with_eid("foo")),
1992 "spaghetti".into()
1993 ),
1994 Expr::val(3),
1995 Expr::val(7)
1996 )
1997 ),
1998 ])
1999 .unwrap(),
2000 "ham".into()
2001 )),
2002 Ok(Value::from(true))
2003 );
2004 assert_matches!(
2006 eval.interpret_inline_policy(&Expr::get_attr(Expr::val(1010122), "hello".into())),
2007 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2008 assert_eq!(expected, nonempty![
2009 Type::Record,
2010 Type::entity_type(
2011 Name::parse_unqualified_name("any_entity_type")
2012 .expect("should be a valid identifier")
2013 ),
2014 ]);
2015 assert_eq!(actual, Type::Long);
2016 assert_eq!(advice, None);
2017 }
2018 );
2019 assert_matches!(
2021 eval.interpret_inline_policy(&Expr::get_attr(Expr::val("hello"), "eggs".into())),
2022 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2023 assert_eq!(expected, nonempty![
2024 Type::Record,
2025 Type::entity_type(
2026 Name::parse_unqualified_name("any_entity_type")
2027 .expect("should be a valid identifier")
2028 ),
2029 ]);
2030 assert_eq!(actual, Type::String);
2031 assert_eq!(advice, None);
2032 }
2033 );
2034 assert_matches!(
2036 eval.interpret_inline_policy(&Expr::has_attr(Expr::val(1010122), "hello".into())),
2037 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2038 assert_eq!(expected, nonempty![
2039 Type::Record,
2040 Type::entity_type(
2041 Name::parse_unqualified_name("any_entity_type")
2042 .expect("should be a valid identifier")
2043 ),
2044 ]);
2045 assert_eq!(actual, Type::Long);
2046 assert_eq!(advice, None);
2047 }
2048 );
2049 assert_matches!(
2051 eval.interpret_inline_policy(&Expr::has_attr(Expr::val("hello"), "eggs".into())),
2052 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2053 assert_eq!(expected, nonempty![
2054 Type::Record,
2055 Type::entity_type(
2056 Name::parse_unqualified_name("any_entity_type")
2057 .expect("should be a valid identifier")
2058 ),
2059 ]);
2060 assert_eq!(actual, Type::String);
2061 assert_eq!(advice, None);
2062 }
2063 );
2064 }
2065
2066 use std::collections::HashSet;
2067
2068 #[test]
2069 fn large_entity_err() {
2070 let expr = Expr::get_attr(
2071 Expr::val(EntityUID::from_str(r#"Foo::"bar""#).unwrap()),
2072 "foo".into(),
2073 );
2074 let attrs = (1..=7)
2075 .map(|id| (format!("{id}").into(), RestrictedExpr::val(true)))
2076 .collect::<HashMap<SmolStr, _>>();
2077 let entity = Entity::new(
2078 r#"Foo::"bar""#.parse().unwrap(),
2079 attrs.clone(),
2080 HashSet::new(),
2081 Extensions::none(),
2082 )
2083 .unwrap();
2084 let request = basic_request();
2085 let entities = Entities::from_entities(
2086 std::iter::once(entity),
2087 None::<&NoEntitiesSchema>,
2088 TCComputation::ComputeNow,
2089 Extensions::none(),
2090 )
2091 .unwrap();
2092 let eval = Evaluator::new(request, &entities, Extensions::none());
2093 let result = eval.interpret_inline_policy(&expr).unwrap_err();
2094 let expected_keys = ["1", "2", "3", "4", "5"]
2096 .into_iter()
2097 .map(|x| x.into())
2098 .collect::<Vec<SmolStr>>();
2099 let expected = EvaluationError::entity_attr_does_not_exist(
2100 Arc::new(r#"Foo::"bar""#.parse().unwrap()),
2101 "foo".into(),
2102 expected_keys.iter(),
2103 7,
2104 None,
2105 );
2106 assert_eq!(result, expected);
2107 }
2108
2109 #[test]
2110 fn large_record_err() {
2111 let expr = Expr::get_attr(
2112 Expr::record((1..=7).map(|id| (format!("{id}").into(), Expr::val(true)))).unwrap(),
2113 "foo".into(),
2114 );
2115 let request = basic_request();
2116 let entities = rich_entities();
2117 let eval = Evaluator::new(request, &entities, Extensions::none());
2118 let result = eval.interpret_inline_policy(&expr).unwrap_err();
2119 let first_five = (1..=5)
2120 .map(|id| format!("{id}").into())
2121 .collect::<Vec<SmolStr>>();
2122 let expected =
2123 EvaluationError::record_attr_does_not_exist("foo".into(), first_five.iter(), 7, None);
2124 assert_eq!(result, expected);
2125 }
2126
2127 #[test]
2128 fn interpret_nots() {
2129 let request = basic_request();
2130 let entities = basic_entities();
2131 let eval = Evaluator::new(request, &entities, Extensions::none());
2132 assert_eq!(
2134 eval.interpret_inline_policy(&Expr::not(Expr::val(true))),
2135 Ok(Value::from(false))
2136 );
2137 assert_eq!(
2139 eval.interpret_inline_policy(&Expr::not(Expr::val(false))),
2140 Ok(Value::from(true))
2141 );
2142 assert_matches!(
2144 eval.interpret_inline_policy(&Expr::not(Expr::val(8))),
2145 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2146 assert_eq!(expected, nonempty![Type::Bool]);
2147 assert_eq!(actual, Type::Long);
2148 assert_eq!(advice, None);
2149 }
2150 );
2151 assert_matches!(
2153 eval.interpret_inline_policy(&Expr::not(Expr::var(Var::Action))),
2154 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2155 assert_eq!(expected, nonempty![Type::Bool]);
2156 assert_eq!(actual, Type::Entity {
2157 ty: EntityUID::test_entity_type(),
2158 });
2159 assert_eq!(advice, None);
2160 }
2161 );
2162 assert_eq!(
2164 eval.interpret_inline_policy(&Expr::not(Expr::not(Expr::val(true)))),
2165 Ok(Value::from(true))
2166 );
2167 assert_eq!(
2169 eval.interpret_inline_policy(&Expr::not(Expr::ite(
2170 Expr::val(true),
2171 Expr::val(false),
2172 Expr::val(true)
2173 ))),
2174 Ok(Value::from(true))
2175 );
2176 assert_eq!(
2178 eval.interpret_inline_policy(&Expr::ite(
2179 Expr::not(Expr::val(true)),
2180 Expr::val("hello"),
2181 Expr::val("goodbye")
2182 )),
2183 Ok(Value::from("goodbye"))
2184 );
2185 }
2186
2187 #[test]
2188 fn interpret_negs() {
2189 let request = basic_request();
2190 let entities = basic_entities();
2191 let eval = Evaluator::new(request, &entities, Extensions::none());
2192 assert_eq!(
2194 eval.interpret_inline_policy(&Expr::neg(Expr::val(101))),
2195 Ok(Value::from(-101))
2196 );
2197 assert_eq!(
2199 eval.interpret_inline_policy(&Expr::neg(Expr::val(-101))),
2200 Ok(Value::from(101))
2201 );
2202 assert_eq!(
2204 eval.interpret_inline_policy(&Expr::neg(Expr::val(0))),
2205 Ok(Value::from(0))
2206 );
2207 assert_eq!(
2209 eval.interpret_inline_policy(&Expr::neg(Expr::neg(Expr::val(7)))),
2210 Ok(Value::from(7))
2211 );
2212 assert_eq!(
2214 eval.interpret_inline_policy(&Expr::ite(
2215 Expr::val(true),
2216 Expr::neg(Expr::val(8)),
2217 Expr::neg(Expr::val(1))
2218 )),
2219 Ok(Value::from(-8))
2220 );
2221 assert_eq!(
2223 eval.interpret_inline_policy(&Expr::neg(Expr::val(Integer::MIN))),
2224 Err(IntegerOverflowError::UnaryOp(UnaryOpOverflowError {
2225 op: UnaryOp::Neg,
2226 arg: Value::from(Integer::MIN),
2227 source_loc: None,
2228 })
2229 .into()),
2230 );
2231 assert_matches!(
2233 eval.interpret_inline_policy(&Expr::neg(Expr::val(false))),
2234 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2235 assert_eq!(expected, nonempty![Type::Long]);
2236 assert_eq!(actual, Type::Bool);
2237 assert_eq!(advice, None);
2238 }
2239 );
2240 assert_matches!(
2242 eval.interpret_inline_policy(&Expr::neg(Expr::set([
2243 Expr::val(1),
2244 Expr::val(2),
2245 Expr::val(3)
2246 ]))),
2247 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2248 assert_eq!(expected, nonempty![Type::Long]);
2249 assert_eq!(actual, Type::Set);
2250 assert_eq!(advice, None);
2251 }
2252 );
2253 }
2254
2255 #[test]
2256 fn interpret_eqs() {
2257 let request = basic_request();
2258 let entities = basic_entities();
2259 let eval = Evaluator::new(request, &entities, Extensions::none());
2260 assert_eq!(
2262 eval.interpret_inline_policy(&Expr::is_eq(Expr::val(33), Expr::val(33))),
2263 Ok(Value::from(true))
2264 );
2265 assert_eq!(
2267 eval.interpret_inline_policy(&Expr::is_eq(Expr::val(33), Expr::val(-12))),
2268 Ok(Value::from(false))
2269 );
2270 assert_eq!(
2272 eval.interpret_inline_policy(&Expr::ite(
2273 Expr::is_eq(Expr::val("foo"), Expr::val("foo")),
2274 Expr::val(12),
2275 Expr::val(97),
2276 )),
2277 Ok(Value::from(12))
2278 );
2279 assert_eq!(
2281 eval.interpret_inline_policy(&Expr::ite(
2282 Expr::is_eq(
2283 Expr::set(vec![Expr::val(1), Expr::val(-33), Expr::val(707)]),
2284 Expr::set(vec![Expr::val(1), Expr::val(-33)])
2285 ),
2286 Expr::val(12),
2287 Expr::val(97),
2288 )),
2289 Ok(Value::from(97))
2290 );
2291 assert_eq!(
2293 eval.interpret_inline_policy(&Expr::is_eq(
2294 Expr::greater(Expr::val(2), Expr::val(0)),
2295 Expr::greater(Expr::val(0), Expr::val(-2))
2296 )),
2297 Ok(Value::from(true))
2298 );
2299 assert_eq!(
2301 eval.interpret_inline_policy(&Expr::is_eq(
2302 Expr::add(Expr::val(12), Expr::val(33)),
2303 Expr::sub(Expr::val(50), Expr::val(5)),
2304 )),
2305 Ok(Value::from(true))
2306 );
2307 assert_eq!(
2309 eval.interpret_inline_policy(&Expr::is_eq(
2310 Expr::set(vec![Expr::val(1), Expr::val(2), Expr::val(40)]),
2311 Expr::set(vec![Expr::val(1), Expr::val(2), Expr::val(40)])
2312 )),
2313 Ok(Value::from(true))
2314 );
2315 assert_eq!(
2317 eval.interpret_inline_policy(&Expr::is_eq(
2318 Expr::set(vec![Expr::val(1), Expr::val(2), Expr::val(40)]),
2319 Expr::set(vec![Expr::val(1), Expr::val(40), Expr::val(2)])
2320 )),
2321 Ok(Value::from(true))
2322 );
2323 assert_eq!(
2325 eval.interpret_inline_policy(&Expr::is_eq(
2326 Expr::set(vec![Expr::val(1), Expr::val(-2), Expr::val(40)]),
2327 Expr::set(vec![Expr::val(1), Expr::val(40)])
2328 )),
2329 Ok(Value::from(false))
2330 );
2331 assert_eq!(
2333 eval.interpret_inline_policy(&Expr::is_eq(
2334 Expr::set(vec![
2335 Expr::val(1),
2336 Expr::val(1),
2337 Expr::val(1),
2338 Expr::val(2),
2339 Expr::val(40)
2340 ]),
2341 Expr::set(vec![Expr::val(40), Expr::val(1), Expr::val(2)])
2342 )),
2343 Ok(Value::from(true))
2344 );
2345 assert_eq!(
2347 eval.interpret_inline_policy(&Expr::is_eq(
2348 Expr::set(vec![
2349 Expr::val(1),
2350 Expr::val(1),
2351 Expr::val(2),
2352 Expr::val(1),
2353 Expr::val(40),
2354 Expr::val(2),
2355 Expr::val(1),
2356 Expr::val(2),
2357 Expr::val(40),
2358 Expr::val(1)
2359 ]),
2360 Expr::set(vec![
2361 Expr::val(1),
2362 Expr::val(40),
2363 Expr::val(1),
2364 Expr::val(2)
2365 ])
2366 )),
2367 Ok(Value::from(true))
2368 );
2369 assert_eq!(
2371 eval.interpret_inline_policy(&Expr::is_eq(
2372 Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2373 Expr::record(vec![
2374 ("os_name".into(), Expr::val("Windows")),
2375 ("manufacturer".into(), Expr::val("ACME Corp")),
2376 ])
2377 .unwrap()
2378 )),
2379 Ok(Value::from(true))
2380 );
2381 assert_eq!(
2383 eval.interpret_inline_policy(&Expr::is_eq(
2384 Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2385 Expr::record(vec![("os_name".into(), Expr::val("Windows"))]).unwrap()
2386 )),
2387 Ok(Value::from(false))
2388 );
2389 assert_eq!(
2391 eval.interpret_inline_policy(&Expr::is_eq(
2392 Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2393 Expr::record(vec![
2394 ("os_name".into(), Expr::val("Windows")),
2395 ("manufacturer".into(), Expr::val("ACME Corp")),
2396 ("extrafield".into(), Expr::val(true)),
2397 ])
2398 .unwrap()
2399 )),
2400 Ok(Value::from(false))
2401 );
2402 assert_eq!(
2404 eval.interpret_inline_policy(&Expr::is_eq(
2405 Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2406 Expr::record(vec![
2407 ("os_name".into(), Expr::val("Windows")),
2408 ("manufacturer".into(), Expr::val("ACME Corp")),
2409 ])
2410 .unwrap()
2411 )),
2412 Ok(Value::from(true))
2413 );
2414 assert_eq!(
2416 eval.interpret_inline_policy(&Expr::is_eq(
2417 Expr::val(EntityUID::with_eid("foo")),
2418 Expr::val(EntityUID::with_eid("foo")),
2419 )),
2420 Ok(Value::from(true))
2421 );
2422 assert_eq!(
2424 eval.interpret_inline_policy(&Expr::is_eq(
2425 Expr::val(EntityUID::with_eid("doesnotexist")),
2426 Expr::val(EntityUID::with_eid("doesnotexist")),
2427 )),
2428 Ok(Value::from(true))
2429 );
2430 assert_eq!(
2432 eval.interpret_inline_policy(&Expr::is_eq(
2433 Expr::val(EntityUID::with_eid("foo")),
2434 Expr::val(EntityUID::with_eid("bar")),
2435 )),
2436 Ok(Value::from(false))
2437 );
2438 assert_eq!(
2440 eval.interpret_inline_policy(&Expr::is_eq(
2441 Expr::val(
2442 EntityUID::with_eid_and_type("type1", "foo")
2443 .expect("should be a valid identifier")
2444 ),
2445 Expr::val(
2446 EntityUID::with_eid_and_type("type2", "bar")
2447 .expect("should be a valid identifier")
2448 ),
2449 )),
2450 Ok(Value::from(false))
2451 );
2452 assert_eq!(
2455 eval.interpret_inline_policy(&Expr::is_eq(
2456 Expr::val(
2457 EntityUID::with_eid_and_type("type1", "foo")
2458 .expect("should be a valid identifier")
2459 ),
2460 Expr::val(
2461 EntityUID::with_eid_and_type("type2", "foo")
2462 .expect("should be a valid identifier")
2463 ),
2464 )),
2465 Ok(Value::from(false))
2466 );
2467 assert_eq!(
2469 eval.interpret_inline_policy(&Expr::is_eq(
2470 Expr::val(EntityUID::with_eid("foo")),
2471 Expr::val(EntityUID::with_eid("doesnotexist")),
2472 )),
2473 Ok(Value::from(false))
2474 );
2475 assert_eq!(
2477 eval.interpret_inline_policy(&Expr::is_eq(
2478 Expr::val("foo"),
2479 Expr::val(EntityUID::with_eid("foo"))
2480 )),
2481 Ok(Value::from(false))
2482 );
2483 }
2484
2485 #[test]
2486 fn interpret_compares() {
2487 let request = basic_request();
2488 let entities = basic_entities();
2489 let eval = Evaluator::new(request, &entities, Extensions::none());
2490 assert_eq!(
2492 eval.interpret_inline_policy(&Expr::less(Expr::val(3), Expr::val(303))),
2493 Ok(Value::from(true))
2494 );
2495 assert_eq!(
2497 eval.interpret_inline_policy(&Expr::less(Expr::val(3), Expr::val(-303))),
2498 Ok(Value::from(false))
2499 );
2500 assert_eq!(
2502 eval.interpret_inline_policy(&Expr::less(Expr::val(-303), Expr::val(-1))),
2503 Ok(Value::from(true))
2504 );
2505 assert_eq!(
2507 eval.interpret_inline_policy(&Expr::less(Expr::val(3), Expr::val(3))),
2508 Ok(Value::from(false))
2509 );
2510 assert_eq!(
2512 eval.interpret_inline_policy(&Expr::lesseq(Expr::val(-33), Expr::val(0))),
2513 Ok(Value::from(true))
2514 );
2515 assert_eq!(
2517 eval.interpret_inline_policy(&Expr::lesseq(Expr::val(3), Expr::val(3))),
2518 Ok(Value::from(true))
2519 );
2520 assert_eq!(
2522 eval.interpret_inline_policy(&Expr::greater(Expr::val(7), Expr::val(3))),
2523 Ok(Value::from(true))
2524 );
2525 assert_eq!(
2527 eval.interpret_inline_policy(&Expr::greater(Expr::val(7), Expr::val(-3))),
2528 Ok(Value::from(true))
2529 );
2530 assert_eq!(
2532 eval.interpret_inline_policy(&Expr::greater(Expr::val(7), Expr::val(7))),
2533 Ok(Value::from(false))
2534 );
2535 assert_eq!(
2537 eval.interpret_inline_policy(&Expr::greatereq(Expr::val(0), Expr::val(-7))),
2538 Ok(Value::from(true))
2539 );
2540 assert_eq!(
2542 eval.interpret_inline_policy(&Expr::greatereq(Expr::val(-1), Expr::val(7))),
2543 Ok(Value::from(false))
2544 );
2545 assert_eq!(
2547 eval.interpret_inline_policy(&Expr::greatereq(Expr::val(7), Expr::val(7))),
2548 Ok(Value::from(true))
2549 );
2550 assert_matches!(
2552 eval.interpret_inline_policy(&Expr::less(Expr::val(false), Expr::val(true))),
2553 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2554 assert_eq!(expected, nonempty![Type::Long]);
2555 assert_eq!(actual, Type::Bool);
2556 assert_eq!(advice, None);
2557 }
2558 );
2559 assert_matches!(
2561 eval.interpret_inline_policy(&Expr::less(Expr::val(false), Expr::val(false))),
2562 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2563 assert_eq!(expected, nonempty![Type::Long]);
2564 assert_eq!(actual, Type::Bool);
2565 assert_eq!(advice, None);
2566 }
2567 );
2568 assert_matches!(
2570 eval.interpret_inline_policy(&Expr::lesseq(Expr::val(true), Expr::val(false))),
2571 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2572 assert_eq!(expected, nonempty![Type::Long]);
2573 assert_eq!(actual, Type::Bool);
2574 assert_eq!(advice, None);
2575 }
2576 );
2577 assert_matches!(
2579 eval.interpret_inline_policy(&Expr::lesseq(Expr::val(false), Expr::val(false))),
2580 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2581 assert_eq!(expected, nonempty![Type::Long]);
2582 assert_eq!(actual, Type::Bool);
2583 assert_eq!(advice, None);
2584 }
2585 );
2586 assert_matches!(
2588 eval.interpret_inline_policy(&Expr::greater(Expr::val(false), Expr::val(true))),
2589 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2590 assert_eq!(expected, nonempty![Type::Long]);
2591 assert_eq!(actual, Type::Bool);
2592 assert_eq!(advice, None);
2593 }
2594 );
2595 assert_matches!(
2597 eval.interpret_inline_policy(&Expr::greater(Expr::val(true), Expr::val(true))),
2598 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2599 assert_eq!(expected, nonempty![Type::Long]);
2600 assert_eq!(actual, Type::Bool);
2601 assert_eq!(advice, None);
2602 }
2603 );
2604 assert_matches!(
2606 eval.interpret_inline_policy(&Expr::greatereq(Expr::val(true), Expr::val(false))),
2607 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2608 assert_eq!(expected, nonempty![Type::Long]);
2609 assert_eq!(actual, Type::Bool);
2610 assert_eq!(advice, None);
2611 }
2612 );
2613 assert_matches!(
2615 eval.interpret_inline_policy(&Expr::greatereq(Expr::val(true), Expr::val(true))),
2616 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2617 assert_eq!(expected, nonempty![Type::Long]);
2618 assert_eq!(actual, Type::Bool);
2619 assert_eq!(advice, None);
2620 }
2621 );
2622 assert_matches!(
2624 eval.interpret_inline_policy(&Expr::less(Expr::val("bc"), Expr::val("zzz"))),
2625 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2626 assert_eq!(expected, nonempty![Type::Long]);
2627 assert_eq!(actual, Type::String);
2628 assert_eq!(advice, None);
2629 }
2630 );
2631 assert_matches!(
2633 eval.interpret_inline_policy(&Expr::less(Expr::val("banana"), Expr::val("zzz"))),
2634 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2635 assert_eq!(expected, nonempty![Type::Long]);
2636 assert_eq!(actual, Type::String);
2637 assert_eq!(advice, None);
2638 }
2639 );
2640 assert_matches!(
2642 eval.interpret_inline_policy(&Expr::less(Expr::val(""), Expr::val("zzz"))),
2643 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2644 assert_eq!(expected, nonempty![Type::Long]);
2645 assert_eq!(actual, Type::String);
2646 assert_eq!(advice, None);
2647 }
2648 );
2649 assert_matches!(
2651 eval.interpret_inline_policy(&Expr::less(Expr::val("a"), Expr::val("1"))),
2652 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2653 assert_eq!(expected, nonempty![Type::Long]);
2654 assert_eq!(actual, Type::String);
2655 assert_eq!(advice, None);
2656 }
2657 );
2658 assert_matches!(
2660 eval.interpret_inline_policy(&Expr::less(Expr::val("a"), Expr::val("A"))),
2661 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2662 assert_eq!(expected, nonempty![Type::Long]);
2663 assert_eq!(actual, Type::String);
2664 assert_eq!(advice, None);
2665 }
2666 );
2667 assert_matches!(
2669 eval.interpret_inline_policy(&Expr::less(Expr::val("A"), Expr::val("A"))),
2670 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2671 assert_eq!(expected, nonempty![Type::Long]);
2672 assert_eq!(actual, Type::String);
2673 assert_eq!(advice, None);
2674 }
2675 );
2676 assert_matches!(
2678 eval.interpret_inline_policy(&Expr::less(Expr::val("zebra"), Expr::val("zebras"))),
2679 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2680 assert_eq!(expected, nonempty![Type::Long]);
2681 assert_eq!(actual, Type::String);
2682 assert_eq!(advice, None);
2683 }
2684 );
2685 assert_matches!(
2687 eval.interpret_inline_policy(&Expr::lesseq(Expr::val("zebra"), Expr::val("zebras"))),
2688 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2689 assert_eq!(expected, nonempty![Type::Long]);
2690 assert_eq!(actual, Type::String);
2691 assert_eq!(advice, None);
2692 }
2693 );
2694 assert_matches!(
2696 eval.interpret_inline_policy(&Expr::lesseq(Expr::val("zebras"), Expr::val("zebras"))),
2697 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2698 assert_eq!(expected, nonempty![Type::Long]);
2699 assert_eq!(actual, Type::String);
2700 assert_eq!(advice, None);
2701 }
2702 );
2703 assert_matches!(
2705 eval.interpret_inline_policy(&Expr::lesseq(Expr::val("zebras"), Expr::val("Zebras"))),
2706 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2707 assert_eq!(expected, nonempty![Type::Long]);
2708 assert_eq!(actual, Type::String);
2709 assert_eq!(advice, None);
2710 }
2711 );
2712 assert_matches!(
2714 eval.interpret_inline_policy(&Expr::greater(Expr::val("123"), Expr::val("78"))),
2715 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2716 assert_eq!(expected, nonempty![Type::Long]);
2717 assert_eq!(actual, Type::String);
2718 assert_eq!(advice, None);
2719 }
2720 );
2721 assert_matches!(
2723 eval.interpret_inline_policy(&Expr::greatereq(
2724 Expr::val(" zebras"),
2725 Expr::val("zebras")
2726 )),
2727 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2728 assert_eq!(expected, nonempty![Type::Long]);
2729 assert_eq!(actual, Type::String);
2730 assert_eq!(advice, None);
2731 }
2732 );
2733 assert_matches!(
2735 eval.interpret_inline_policy(&Expr::greatereq(Expr::val(""), Expr::val(""))),
2736 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2737 assert_eq!(expected, nonempty![Type::Long]);
2738 assert_eq!(actual, Type::String);
2739 assert_eq!(advice, None);
2740 }
2741 );
2742 assert_matches!(
2744 eval.interpret_inline_policy(&Expr::greatereq(Expr::val(""), Expr::val("_hi"))),
2745 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2746 assert_eq!(expected, nonempty![Type::Long]);
2747 assert_eq!(actual, Type::String);
2748 assert_eq!(advice, None);
2749 }
2750 );
2751 assert_matches!(
2753 eval.interpret_inline_policy(&Expr::greatereq(Expr::val("🦀"), Expr::val("_hi"))),
2754 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2755 assert_eq!(expected, nonempty![Type::Long]);
2756 assert_eq!(actual, Type::String);
2757 assert_eq!(advice, None);
2758 }
2759 );
2760 assert_matches!(
2762 eval.interpret_inline_policy(&Expr::less(Expr::val(2), Expr::val("4"))),
2763 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2764 assert_eq!(expected, nonempty![Type::Long]);
2765 assert_eq!(actual, Type::String);
2766 assert_eq!(advice, None);
2767 }
2768 );
2769 assert_matches!(
2771 eval.interpret_inline_policy(&Expr::less(Expr::val("4"), Expr::val(2))),
2772 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2773 assert_eq!(expected, nonempty![Type::Long]);
2774 assert_eq!(actual, Type::String);
2775 assert_eq!(advice, None);
2776 }
2777 );
2778 assert_matches!(
2780 eval.interpret_inline_policy(&Expr::less(Expr::val(false), Expr::val(1))),
2781 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2782 assert_eq!(expected, nonempty![Type::Long]);
2783 assert_eq!(actual, Type::Bool);
2784 assert_eq!(advice, None);
2785 }
2786 );
2787 assert_matches!(
2789 eval.interpret_inline_policy(&Expr::less(Expr::val(1), Expr::val(false))),
2790 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2791 assert_eq!(expected, nonempty![Type::Long]);
2792 assert_eq!(actual, Type::Bool);
2793 assert_eq!(advice, None);
2794 }
2795 );
2796 assert_matches!(
2798 eval.interpret_inline_policy(&Expr::less(
2799 Expr::set(vec![Expr::val(1), Expr::val(2)]),
2800 Expr::set(vec![Expr::val(47), Expr::val(0)])
2801 )),
2802 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2803 assert_eq!(expected, nonempty![Type::Long]);
2804 assert_eq!(actual, Type::Set);
2805 assert_eq!(advice, None);
2806 }
2807 );
2808 }
2809
2810 #[test]
2811 fn interpret_comparison_err_order() {
2812 let request = basic_request();
2816 let entities = basic_entities();
2817 let eval = Evaluator::new(request, &entities, Extensions::none());
2818
2819 assert_matches!(
2820 eval.interpret_inline_policy(&Expr::greatereq(
2821 Expr::add(Expr::val("a"), Expr::val("b")),
2822 Expr::add(Expr::val(false), Expr::val(true))
2823 )),
2824 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2825 assert_eq!(expected, nonempty![Type::Long]);
2826 assert_eq!(actual, Type::String);
2827 assert_eq!(advice, None);
2828 }
2829 );
2830
2831 assert_matches!(
2832 eval.interpret_inline_policy(&Expr::greater(
2833 Expr::add(Expr::val("a"), Expr::val("b")),
2834 Expr::add(Expr::val(false), Expr::val(true))
2835 )),
2836 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2837 assert_eq!(expected, nonempty![Type::Long]);
2838 assert_eq!(actual, Type::String);
2839 assert_eq!(advice, None);
2840 }
2841 );
2842
2843 assert_matches!(
2844 eval.interpret_inline_policy(&Expr::lesseq(
2845 Expr::add(Expr::val("a"), Expr::val("b")),
2846 Expr::add(Expr::val(false), Expr::val(true))
2847 )),
2848 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2849 assert_eq!(expected, nonempty![Type::Long]);
2850 assert_eq!(actual, Type::String);
2851 assert_eq!(advice, None);
2852 }
2853 );
2854
2855 assert_matches!(
2856 eval.interpret_inline_policy(&Expr::less(
2857 Expr::add(Expr::val("a"), Expr::val("b")),
2858 Expr::add(Expr::val(false), Expr::val(true))
2859 )),
2860 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2861 assert_eq!(expected, nonempty![Type::Long]);
2862 assert_eq!(actual, Type::String);
2863 assert_eq!(advice, None);
2864 }
2865 );
2866 }
2867
2868 #[test]
2869 fn interpret_arithmetic() {
2870 let request = basic_request();
2871 let entities = basic_entities();
2872 let eval = Evaluator::new(request, &entities, Extensions::none());
2873 assert_eq!(
2875 eval.interpret_inline_policy(&Expr::add(Expr::val(11), Expr::val(22))),
2876 Ok(Value::from(33))
2877 );
2878 assert_eq!(
2880 eval.interpret_inline_policy(&Expr::add(Expr::val(11), Expr::val(0))),
2881 Ok(Value::from(11))
2882 );
2883 assert_eq!(
2885 eval.interpret_inline_policy(&Expr::add(Expr::val(-1), Expr::val(1))),
2886 Ok(Value::from(0))
2887 );
2888 assert_eq!(
2890 eval.interpret_inline_policy(&Expr::add(Expr::val(Integer::MAX), Expr::val(1))),
2891 Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
2892 op: BinaryOp::Add,
2893 arg1: Value::from(Integer::MAX),
2894 arg2: Value::from(1),
2895 source_loc: None,
2896 })
2897 .into())
2898 );
2899 assert_matches!(
2901 eval.interpret_inline_policy(&Expr::add(Expr::val(7), Expr::val("3"))),
2902 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2903 assert_eq!(expected, nonempty![Type::Long]);
2904 assert_eq!(actual, Type::String);
2905 assert_eq!(advice, None);
2906 }
2907 );
2908 assert_eq!(
2910 eval.interpret_inline_policy(&Expr::sub(Expr::val(44), Expr::val(31))),
2911 Ok(Value::from(13))
2912 );
2913 assert_eq!(
2915 eval.interpret_inline_policy(&Expr::sub(Expr::val(5), Expr::val(-3))),
2916 Ok(Value::from(8))
2917 );
2918 assert_eq!(
2920 eval.interpret_inline_policy(&Expr::sub(Expr::val(Integer::MIN + 2), Expr::val(3))),
2921 Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
2922 op: BinaryOp::Sub,
2923 arg1: Value::from(Integer::MIN + 2),
2924 arg2: Value::from(3),
2925 source_loc: None,
2926 })
2927 .into())
2928 );
2929 assert_matches!(
2931 eval.interpret_inline_policy(&Expr::sub(Expr::val("ham"), Expr::val("ha"))),
2932 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2933 assert_eq!(expected, nonempty![Type::Long]);
2934 assert_eq!(actual, Type::String);
2935 assert_eq!(advice, None);
2936 }
2937 );
2938 assert_eq!(
2940 eval.interpret_inline_policy(&Expr::mul(Expr::val(5), Expr::val(-3))),
2941 Ok(Value::from(-15))
2942 );
2943 assert_eq!(
2945 eval.interpret_inline_policy(&Expr::mul(Expr::val(5), Expr::val(0))),
2946 Ok(Value::from(0))
2947 );
2948 assert_matches!(
2950 eval.interpret_inline_policy(&Expr::mul(Expr::val("5"), Expr::val(0))),
2951 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2952 assert_eq!(expected, nonempty![Type::Long]);
2953 assert_eq!(actual, Type::String);
2954 assert_eq!(advice, None);
2955 }
2956 );
2957 assert_eq!(
2959 eval.interpret_inline_policy(&Expr::mul(Expr::val(Integer::MAX - 1), Expr::val(3))),
2960 Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
2961 op: BinaryOp::Mul,
2962 arg1: Value::from(Integer::MAX - 1),
2963 arg2: Value::from(3),
2964 source_loc: None,
2965 })
2966 .into())
2967 );
2968 }
2969
2970 #[test]
2971 fn interpret_set_and_map_membership() {
2972 let request = basic_request();
2973 let entities = rich_entities();
2974 let eval = Evaluator::new(request, &entities, Extensions::none());
2975
2976 assert_eq!(
2978 eval.interpret_inline_policy(&Expr::contains(
2979 Expr::set(vec![Expr::val(2), Expr::val(3), Expr::val(4)]),
2980 Expr::val(2)
2981 )),
2982 Ok(Value::from(true))
2983 );
2984 assert_eq!(
2986 eval.interpret_inline_policy(&Expr::contains(
2987 Expr::set(vec![Expr::val(34), Expr::val(2), Expr::val(-7)]),
2988 Expr::val(2)
2989 )),
2990 Ok(Value::from(true))
2991 );
2992 assert_eq!(
2994 eval.interpret_inline_policy(&Expr::contains(
2995 Expr::set(vec![Expr::val(34), Expr::val(2), Expr::val(-7)]),
2996 Expr::val(3)
2997 )),
2998 Ok(Value::from(false))
2999 );
3000 assert_eq!(
3002 eval.interpret_inline_policy(&Expr::contains(Expr::set(vec![]), Expr::val(7))),
3003 Ok(Value::from(false))
3004 );
3005 assert_eq!(
3007 eval.interpret_inline_policy(&Expr::contains(
3008 Expr::set(vec![
3009 Expr::val("some"),
3010 Expr::val("useful"),
3011 Expr::val("tags")
3012 ]),
3013 Expr::val("foo")
3014 )),
3015 Ok(Value::from(false))
3016 );
3017 assert_eq!(
3019 eval.interpret_inline_policy(&Expr::contains(
3020 Expr::set(vec![
3021 Expr::val("some"),
3022 Expr::val("useful"),
3023 Expr::val("tags")
3024 ]),
3025 Expr::val("useful")
3026 )),
3027 Ok(Value::from(true))
3028 );
3029 assert_eq!(
3031 eval.interpret_inline_policy(&Expr::contains(
3032 Expr::set(vec![
3033 Expr::val(EntityUID::with_eid("child")),
3034 Expr::val(EntityUID::with_eid("sibling"))
3035 ]),
3036 Expr::val(EntityUID::with_eid("child"))
3037 )),
3038 Ok(Value::from(true))
3039 );
3040 assert_eq!(
3042 eval.interpret_inline_policy(&Expr::contains(
3043 Expr::set(vec![
3044 Expr::val(EntityUID::with_eid("parent")),
3045 Expr::val(EntityUID::with_eid("sibling"))
3046 ]),
3047 Expr::val(EntityUID::with_eid("child"))
3048 )),
3049 Ok(Value::from(false))
3050 );
3051 assert_eq!(
3053 eval.interpret_inline_policy(&Expr::contains(
3054 Expr::set(vec![Expr::val("foo"), Expr::val("bar")]),
3055 Expr::val(3)
3056 )),
3057 Ok(Value::from(false))
3058 );
3059 assert_eq!(
3061 eval.interpret_inline_policy(&Expr::contains(
3062 Expr::set(vec![Expr::val("foo"), Expr::val("bar")]),
3063 Expr::set(vec![Expr::val(3)])
3064 )),
3065 Ok(Value::from(false))
3066 );
3067 assert_eq!(
3069 eval.interpret_inline_policy(&Expr::contains(
3070 Expr::set(vec![
3071 Expr::set(vec![Expr::val(7)]),
3072 Expr::val("eggs"),
3073 Expr::set(vec![Expr::val(3)])
3074 ]),
3075 Expr::set(vec![Expr::val(3)])
3076 )),
3077 Ok(Value::from(true))
3078 );
3079
3080 assert_eq!(
3082 eval.interpret_inline_policy(&Expr::contains(
3083 Expr::set(vec![
3084 Expr::val("2"),
3085 Expr::val(20),
3086 Expr::val(true),
3087 Expr::val(EntityUID::with_eid("foo")),
3088 ]),
3089 Expr::val(2)
3090 )),
3091 Ok(Value::from(false))
3092 );
3093 assert_eq!(
3095 eval.interpret_inline_policy(&Expr::contains(
3096 Expr::set(vec![
3097 Expr::val("ham"),
3098 Expr::get_attr(
3099 Expr::get_attr(
3100 Expr::val(EntityUID::with_eid("entity_with_attrs")),
3101 "address".into()
3102 ),
3103 "town".into()
3104 ),
3105 Expr::val(-1),
3106 ]),
3107 Expr::val("barmstadt")
3108 )),
3109 Ok(Value::from(true))
3110 );
3111 assert_matches!(
3113 eval.interpret_inline_policy(&Expr::contains(Expr::val(3), Expr::val(7))),
3114 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3115 assert_eq!(expected, nonempty![Type::Set]);
3116 assert_eq!(actual, Type::Long);
3117 assert_eq!(advice, None);
3118 }
3119 );
3120 assert_matches!(
3122 eval.interpret_inline_policy(&Expr::contains(
3123 Expr::record(vec![("ham".into(), Expr::val("eggs"))]).unwrap(),
3124 Expr::val("ham")
3125 )),
3126 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3127 assert_eq!(expected, nonempty![Type::Set]);
3128 assert_eq!(actual, Type::Record);
3129 assert_eq!(advice, None);
3130 }
3131 );
3132 assert_matches!(
3134 eval.interpret_inline_policy(&Expr::contains(
3135 Expr::val(3),
3136 Expr::set(vec![Expr::val(1), Expr::val(3), Expr::val(7)])
3137 )),
3138 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3139 assert_eq!(expected, nonempty![Type::Set]);
3140 assert_eq!(actual, Type::Long);
3141 assert_eq!(advice, None);
3142 }
3143 );
3144 }
3145
3146 #[test]
3147 fn interpret_hierarchy_membership() {
3148 let request = basic_request();
3149 let entities = rich_entities();
3150 let eval = Evaluator::new(request, &entities, Extensions::none());
3151 assert_eq!(
3153 eval.interpret_inline_policy(&Expr::is_in(
3154 Expr::val(EntityUID::with_eid("child")),
3155 Expr::val(EntityUID::with_eid("unrelated"))
3156 )),
3157 Ok(Value::from(false))
3158 );
3159 assert_eq!(
3161 eval.interpret_inline_policy(&Expr::is_in(
3162 Expr::val(EntityUID::with_eid("child")),
3163 Expr::val(EntityUID::with_eid("parent"))
3164 )),
3165 Ok(Value::from(true))
3166 );
3167 assert_eq!(
3169 eval.interpret_inline_policy(&Expr::is_in(
3170 Expr::val(
3171 EntityUID::with_eid_and_type("other_type", "other_child")
3172 .expect("should be a valid identifier")
3173 ),
3174 Expr::val(EntityUID::with_eid("parent"))
3175 )),
3176 Ok(Value::from(true))
3177 );
3178 assert_eq!(
3180 eval.interpret_inline_policy(&Expr::is_in(
3181 Expr::val(
3182 EntityUID::with_eid_and_type("other_type", "other_child")
3183 .expect("should be a valid identifier")
3184 ),
3185 Expr::val(EntityUID::with_eid("unrelated"))
3186 )),
3187 Ok(Value::from(false))
3188 );
3189 assert_eq!(
3191 eval.interpret_inline_policy(&Expr::is_in(
3192 Expr::val(EntityUID::with_eid("child")),
3193 Expr::val(EntityUID::with_eid("sibling"))
3194 )),
3195 Ok(Value::from(false))
3196 );
3197 assert_eq!(
3199 eval.interpret_inline_policy(&Expr::is_in(
3200 Expr::val(EntityUID::with_eid("parent")),
3201 Expr::val(EntityUID::with_eid("parent"))
3202 )),
3203 Ok(Value::from(true))
3204 );
3205 assert_eq!(
3207 eval.interpret_inline_policy(&Expr::is_in(
3208 Expr::val(EntityUID::with_eid("doesnotexist")),
3209 Expr::val(EntityUID::with_eid("doesnotexist")),
3210 )),
3211 Ok(Value::from(true))
3212 );
3213 assert_eq!(
3215 eval.interpret_inline_policy(&Expr::is_in(
3216 Expr::val(EntityUID::with_eid("parent")),
3217 Expr::val(EntityUID::with_eid("child"))
3218 )),
3219 Ok(Value::from(false))
3220 );
3221 assert_eq!(
3223 eval.interpret_inline_policy(&Expr::is_in(
3224 Expr::val(EntityUID::with_eid("child")),
3225 Expr::val(EntityUID::with_eid("grandparent"))
3226 )),
3227 Ok(Value::from(true))
3228 );
3229 assert_eq!(
3231 eval.interpret_inline_policy(&Expr::is_in(
3232 Expr::val(EntityUID::with_eid("doesnotexist")),
3233 Expr::val(EntityUID::with_eid("parent"))
3234 )),
3235 Ok(Value::from(false))
3236 );
3237 assert_eq!(
3239 eval.interpret_inline_policy(&Expr::is_in(
3240 Expr::val(EntityUID::with_eid("parent")),
3241 Expr::val(EntityUID::with_eid("doesnotexist"))
3242 )),
3243 Ok(Value::from(false))
3244 );
3245 assert_eq!(
3247 eval.interpret_inline_policy(&Expr::is_in(
3248 Expr::val(EntityUID::with_eid("child")),
3249 Expr::set(vec![
3250 Expr::val(EntityUID::with_eid("grandparent")),
3251 Expr::val(EntityUID::with_eid("sibling")),
3252 ])
3253 )),
3254 Ok(Value::from(true))
3255 );
3256 assert_eq!(
3258 eval.interpret_inline_policy(&Expr::is_in(
3259 Expr::val(EntityUID::with_eid("child")),
3260 Expr::set(vec![
3261 Expr::val(EntityUID::with_eid("sibling")),
3262 Expr::val(EntityUID::with_eid("grandparent")),
3263 ])
3264 )),
3265 Ok(Value::from(true))
3266 );
3267 assert_eq!(
3269 eval.interpret_inline_policy(&Expr::is_in(
3270 Expr::val(EntityUID::with_eid("child")),
3271 Expr::set(vec![
3272 Expr::val(EntityUID::with_eid("sibling")),
3273 Expr::val(EntityUID::with_eid("unrelated")),
3274 ])
3275 )),
3276 Ok(Value::from(false))
3277 );
3278 assert_eq!(
3280 eval.interpret_inline_policy(&Expr::is_in(
3281 Expr::val(EntityUID::with_eid("child")),
3282 Expr::set(vec![
3283 Expr::val(EntityUID::with_eid("unrelated")),
3284 Expr::val(EntityUID::with_eid("child")),
3285 ])
3286 )),
3287 Ok(Value::from(true))
3288 );
3289 assert_eq!(
3291 eval.interpret_inline_policy(&Expr::is_in(
3292 Expr::val(EntityUID::with_eid("child")),
3293 Expr::set(vec![
3294 Expr::val(EntityUID::with_eid("child")),
3295 Expr::val(EntityUID::with_eid("unrelated")),
3296 ])
3297 )),
3298 Ok(Value::from(true))
3299 );
3300 assert_matches!(
3302 eval.interpret_inline_policy(&Expr::is_in(
3303 Expr::val(EntityUID::with_eid("child")),
3304 Expr::set(vec![
3305 Expr::val(EntityUID::with_eid("child")),
3306 Expr::val(true),
3307 ])
3308 )),
3309 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3310 assert_eq!(expected, nonempty![Type::entity_type(
3311 Name::parse_unqualified_name("any_entity_type")
3312 .expect("should be a valid identifier")
3313 )]);
3314 assert_eq!(actual, Type::Bool);
3315 assert_eq!(advice, None);
3316 }
3317 );
3318 assert_eq!(
3320 eval.interpret_inline_policy(&Expr::is_in(
3321 Expr::val(EntityUID::with_eid("doesnotexistA")),
3322 Expr::set(vec![
3323 Expr::val(EntityUID::with_eid("doesnotexistA")),
3324 Expr::val(EntityUID::with_eid("doesnotexistB")),
3325 ])
3326 )),
3327 Ok(Value::from(true))
3328 );
3329 assert_eq!(
3331 eval.interpret_inline_policy(&Expr::is_in(
3332 Expr::val(EntityUID::with_eid("doesnotexistA")),
3333 Expr::set(vec![
3334 Expr::val(EntityUID::with_eid("doesnotexistB")),
3335 Expr::val(EntityUID::with_eid("doesnotexistC")),
3336 ])
3337 )),
3338 Ok(Value::from(false))
3339 );
3340 assert_eq!(
3342 eval.interpret_inline_policy(&Expr::is_in(
3343 Expr::val(EntityUID::with_eid("child")),
3344 Expr::set(vec![
3345 Expr::val(EntityUID::with_eid("doesnotexistB")),
3346 Expr::val(EntityUID::with_eid("doesnotexistC")),
3347 ])
3348 )),
3349 Ok(Value::from(false))
3350 );
3351 assert_eq!(
3353 eval.interpret_inline_policy(&Expr::is_in(
3354 Expr::val(EntityUID::with_eid("doesnotexistA")),
3355 Expr::set(vec![
3356 Expr::val(EntityUID::with_eid("child")),
3357 Expr::val(EntityUID::with_eid("grandparent")),
3358 ])
3359 )),
3360 Ok(Value::from(false))
3361 );
3362 assert_matches!(
3364 eval.interpret_inline_policy(&Expr::is_in(Expr::val("foo"), Expr::val("foobar"))),
3365 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3366 assert_eq!(expected, nonempty![Type::entity_type(
3367 Name::parse_unqualified_name("any_entity_type")
3368 .expect("should be a valid identifier")
3369 )]);
3370 assert_eq!(actual, Type::String);
3371 assert_eq!(advice, None);
3372 }
3373 );
3374 assert_matches!(
3376 eval.interpret_inline_policy(&Expr::is_in(
3377 Expr::val("spoon"),
3378 Expr::val(EntityUID::with_eid("entity_with_attrs"))
3379 )),
3380 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3381 assert_eq!(expected, nonempty![Type::entity_type(
3382 Name::parse_unqualified_name("any_entity_type")
3383 .expect("should be a valid identifier")
3384 )]);
3385 assert_eq!(actual, Type::String);
3386 assert_eq!(advice, None);
3387 }
3388 );
3389 assert_matches!(
3391 eval.interpret_inline_policy(&Expr::is_in(
3392 Expr::val(3),
3393 Expr::set(vec![Expr::val(34), Expr::val(-2), Expr::val(7)])
3394 )),
3395 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3396 assert_eq!(expected, nonempty![Type::entity_type(
3397 Name::parse_unqualified_name("any_entity_type")
3398 .expect("should be a valid identifier")
3399 )]);
3400 assert_eq!(actual, Type::Long);
3401 assert_eq!(advice, Some("`in` is for checking the entity hierarchy; use `.contains()` to test set membership".into()));
3402 }
3403 );
3404 assert_matches!(
3406 eval.interpret_inline_policy(&Expr::is_in(
3407 Expr::val("foo"),
3408 Expr::record(vec![
3409 ("foo".into(), Expr::val(2)),
3410 ("bar".into(), Expr::val(true)),
3411 ]).unwrap()
3412 )),
3413 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3414 assert_eq!(expected, nonempty![Type::entity_type(
3415 Name::parse_unqualified_name("any_entity_type")
3416 .expect("should be a valid identifier")
3417 )]);
3418 assert_eq!(actual, Type::String);
3419 assert_eq!(advice, Some("`in` is for checking the entity hierarchy; use `has` to test if a record has a key".into()));
3420 }
3421 );
3422 assert_matches!(
3424 eval.interpret_inline_policy(&Expr::is_in(
3425 Expr::val(EntityUID::with_eid("child")),
3426 Expr::record(vec![
3427 ("foo".into(), Expr::val(2)),
3428 ("bar".into(), Expr::val(true)),
3429 ])
3430 .unwrap()
3431 )),
3432 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3433 assert_eq!(expected, nonempty![
3434 Type::Set,
3435 Type::entity_type(
3436 Name::parse_unqualified_name("any_entity_type")
3437 .expect("should be a valid identifier")
3438 )
3439 ]);
3440 assert_eq!(actual, Type::Record);
3441 assert_eq!(advice, None);
3442 }
3443 );
3444 }
3445
3446 #[test]
3447 fn interpret_hierarchy_membership_slice() {
3448 let request = Request::new(
3454 (EntityUID::with_eid("Alice"), None),
3455 (EntityUID::with_eid("test_action"), None),
3456 (EntityUID::with_eid("test_resource"), None),
3457 Context::empty(),
3458 Some(&RequestSchemaAllPass),
3459 Extensions::none(),
3460 )
3461 .unwrap();
3462 let mut alice = Entity::with_uid(EntityUID::with_eid("Alice"));
3464 let parent = Entity::with_uid(EntityUID::with_eid("Friends"));
3465 alice.add_ancestor(parent.uid().clone());
3466 let entities = Entities::from_entities(
3467 vec![alice],
3468 None::<&NoEntitiesSchema>,
3469 TCComputation::AssumeAlreadyComputed,
3470 Extensions::all_available(),
3471 )
3472 .expect("failed to create basic entities");
3473 let eval = Evaluator::new(request, &entities, Extensions::none());
3474 assert_eq!(
3475 eval.interpret_inline_policy(&Expr::is_in(
3476 Expr::val(EntityUID::with_eid("Alice")),
3477 Expr::val(EntityUID::with_eid("Friends"))
3478 )),
3479 Ok(Value::from(true))
3480 );
3481 assert_eq!(
3482 eval.interpret_inline_policy(&Expr::is_in(
3483 Expr::val(EntityUID::with_eid("Bob")),
3484 Expr::val(EntityUID::with_eid("Friends"))
3485 )),
3486 Ok(Value::from(false))
3487 );
3488 assert_eq!(
3489 eval.interpret_inline_policy(&Expr::is_in(
3490 Expr::val(EntityUID::with_eid("Alice")),
3491 Expr::set(vec![
3492 Expr::val(EntityUID::with_eid("Friends")),
3493 Expr::val(EntityUID::with_eid("Bob"))
3494 ])
3495 )),
3496 Ok(Value::from(true))
3497 );
3498 assert_eq!(
3499 eval.interpret_inline_policy(&Expr::is_in(
3500 Expr::val(EntityUID::with_eid("Bob")),
3501 Expr::set(vec![
3502 Expr::val(EntityUID::with_eid("Friends")),
3503 Expr::val(EntityUID::with_eid("Alice"))
3504 ])
3505 )),
3506 Ok(Value::from(false))
3507 );
3508 }
3509
3510 #[test]
3511 fn interpret_string_like() {
3512 let request = basic_request();
3513 let entities = basic_entities();
3514 let eval = Evaluator::new(request, &entities, Extensions::none());
3515 assert_eq!(
3517 eval.interpret_inline_policy(
3518 &parse_expr(r#""eggs" like "ham*""#).expect("parsing error")
3519 ),
3520 Ok(Value::from(false))
3521 );
3522 assert_eq!(
3523 eval.interpret_inline_policy(
3524 &parse_expr(r#""eggs" like "*ham""#).expect("parsing error")
3525 ),
3526 Ok(Value::from(false))
3527 );
3528 assert_eq!(
3529 eval.interpret_inline_policy(
3530 &parse_expr(r#""eggs" like "*ham*""#).expect("parsing error")
3531 ),
3532 Ok(Value::from(false))
3533 );
3534 assert_eq!(
3536 eval.interpret_inline_policy(
3537 &parse_expr(r#""ham and eggs" like "ham*""#).expect("parsing error")
3538 ),
3539 Ok(Value::from(true))
3540 );
3541 assert_eq!(
3542 eval.interpret_inline_policy(
3543 &parse_expr(r#""ham and eggs" like "*ham""#).expect("parsing error")
3544 ),
3545 Ok(Value::from(false))
3546 );
3547 assert_eq!(
3548 eval.interpret_inline_policy(
3549 &parse_expr(r#""ham and eggs" like "*ham*""#).expect("parsing error")
3550 ),
3551 Ok(Value::from(true))
3552 );
3553 assert_eq!(
3554 eval.interpret_inline_policy(
3555 &parse_expr(r#""ham and eggs" like "*h*a*m*""#).expect("parsing error")
3556 ),
3557 Ok(Value::from(true))
3558 );
3559 assert_eq!(
3561 eval.interpret_inline_policy(
3562 &parse_expr(r#""eggs and ham" like "ham*""#).expect("parsing error")
3563 ),
3564 Ok(Value::from(false))
3565 );
3566 assert_eq!(
3567 eval.interpret_inline_policy(
3568 &parse_expr(r#""eggs and ham" like "*ham""#).expect("parsing error")
3569 ),
3570 Ok(Value::from(true))
3571 );
3572 assert_eq!(
3574 eval.interpret_inline_policy(
3575 &parse_expr(r#""eggs, ham, and spinach" like "ham*""#).expect("parsing error")
3576 ),
3577 Ok(Value::from(false))
3578 );
3579 assert_eq!(
3580 eval.interpret_inline_policy(
3581 &parse_expr(r#""eggs, ham, and spinach" like "*ham""#).expect("parsing error")
3582 ),
3583 Ok(Value::from(false))
3584 );
3585 assert_eq!(
3586 eval.interpret_inline_policy(
3587 &parse_expr(r#""eggs, ham, and spinach" like "*ham*""#).expect("parsing error")
3588 ),
3589 Ok(Value::from(true))
3590 );
3591 assert_eq!(
3593 eval.interpret_inline_policy(
3594 &parse_expr(r#""Gotham" like "ham*""#).expect("parsing error")
3595 ),
3596 Ok(Value::from(false))
3597 );
3598 assert_eq!(
3599 eval.interpret_inline_policy(
3600 &parse_expr(r#""Gotham" like "*ham""#).expect("parsing error")
3601 ),
3602 Ok(Value::from(true))
3603 );
3604 assert_eq!(
3606 eval.interpret_inline_policy(
3607 &parse_expr(r#""ham" like "ham""#).expect("parsing error")
3608 ),
3609 Ok(Value::from(true))
3610 );
3611 assert_eq!(
3612 eval.interpret_inline_policy(
3613 &parse_expr(r#""ham" like "ham*""#).expect("parsing error")
3614 ),
3615 Ok(Value::from(true))
3616 );
3617 assert_eq!(
3618 eval.interpret_inline_policy(
3619 &parse_expr(r#""ham" like "*ham""#).expect("parsing error")
3620 ),
3621 Ok(Value::from(true))
3622 );
3623 assert_eq!(
3624 eval.interpret_inline_policy(
3625 &parse_expr(r#""ham" like "*h*a*m*""#).expect("parsing error")
3626 ),
3627 Ok(Value::from(true))
3628 );
3629 assert_eq!(
3631 eval.interpret_inline_policy(
3632 &parse_expr(r#""ham and ham" like "ham*""#).expect("parsing error")
3633 ),
3634 Ok(Value::from(true))
3635 );
3636 assert_eq!(
3637 eval.interpret_inline_policy(
3638 &parse_expr(r#""ham and ham" like "*ham""#).expect("parsing error")
3639 ),
3640 Ok(Value::from(true))
3641 );
3642 assert_eq!(
3644 eval.interpret_inline_policy(
3645 &parse_expr(r#""ham" like "*ham and eggs*""#).expect("parsing error")
3646 ),
3647 Ok(Value::from(false))
3648 );
3649 assert_matches!(
3651 eval.interpret_inline_policy(&Expr::like(Expr::val(354), vec![])),
3652 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3653 assert_eq!(expected, nonempty![Type::String]);
3654 assert_eq!(actual, Type::Long);
3655 assert_eq!(advice, None);
3656 }
3657 );
3658 assert_matches!(
3660 eval.interpret_inline_policy(&Expr::contains(
3661 Expr::val("ham and ham"),
3662 Expr::val("ham")
3663 )),
3664 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3665 assert_eq!(expected, nonempty![Type::Set]);
3666 assert_eq!(actual, Type::String);
3667 assert_eq!(advice, None);
3668 }
3669 );
3670 assert_eq!(
3672 eval.interpret_inline_policy(&Expr::like(
3673 Expr::val("*"),
3674 vec![PatternElem::Char('\u{0000}')]
3675 )),
3676 Ok(Value::from(false))
3677 );
3678
3679 assert_eq!(
3680 eval.interpret_inline_policy(
3681 &parse_expr(r#" "\\afterslash" like "\\*" "#).expect("parsing error")
3682 ),
3683 Ok(Value::from(true))
3684 );
3685 }
3686
3687 #[test]
3688 fn interpret_string_like_escaped_chars() {
3689 let request = basic_request();
3690 let entities = basic_entities();
3691 let eval = Evaluator::new(request, &entities, Extensions::none());
3692 assert_eq!(
3694 eval.interpret_inline_policy(
3695 &parse_expr(r#""string\\with\\backslashes" like "string\\with\\backslashes""#)
3696 .expect("parsing error")
3697 ),
3698 Ok(Value::from(true))
3699 );
3700 assert_eq!(
3701 eval.interpret_inline_policy(
3702 &parse_expr(
3703 r#""string\\with\\backslashes" like "string\u{0000}with\u{0000}backslashe""#
3704 )
3705 .expect("parsing error")
3706 ),
3707 Ok(Value::from(false))
3708 );
3709 assert_eq!(
3710 eval.interpret_inline_policy(
3711 &parse_expr(r#""string\\with\\backslashes" like "string*with*backslashes""#)
3712 .expect("parsing error")
3713 ),
3714 Ok(Value::from(true))
3715 );
3716 assert_eq!(
3717 eval.interpret_inline_policy(
3718 &parse_expr(r#""string*with*stars" like "string\*with\*stars""#)
3719 .expect("parsing error")
3720 ),
3721 Ok(Value::from(true))
3722 );
3723 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)));
3724 }
3725
3726 #[test]
3727 fn interpret_is() {
3728 let request = basic_request();
3729 let entities = basic_entities();
3730 let eval = Evaluator::new(request, &entities, Extensions::none());
3731 assert_eq!(
3732 eval.interpret_inline_policy(
3733 &parse_expr(&format!(
3734 r#"principal is {}"#,
3735 EntityUID::test_entity_type()
3736 ))
3737 .expect("parsing error")
3738 ),
3739 Ok(Value::from(true))
3740 );
3741 assert_eq!(
3742 eval.interpret_inline_policy(
3743 &parse_expr(&format!(
3744 r#"principal is N::S::{}"#,
3745 EntityUID::test_entity_type()
3746 ))
3747 .expect("parsing error")
3748 ),
3749 Ok(Value::from(false))
3750 );
3751 assert_eq!(
3752 eval.interpret_inline_policy(
3753 &parse_expr(r#"User::"alice" is User"#).expect("parsing error")
3754 ),
3755 Ok(Value::from(true))
3756 );
3757 assert_eq!(
3758 eval.interpret_inline_policy(
3759 &parse_expr(r#"User::"alice" is Group"#).expect("parsing error")
3760 ),
3761 Ok(Value::from(false))
3762 );
3763 assert_eq!(
3764 eval.interpret_inline_policy(
3765 &parse_expr(r#"N::S::User::"alice" is N::S::User"#).expect("parsing error")
3766 ),
3767 Ok(Value::from(true))
3768 );
3769 assert_eq!(
3770 eval.interpret_inline_policy(
3771 &parse_expr(r#"N::S::User::"alice" is User"#).expect("parsing error")
3772 ),
3773 Ok(Value::from(false))
3774 );
3775 assert_matches!(
3776 eval.interpret_inline_policy(&parse_expr(r#"1 is Group"#).expect("parsing error")),
3777 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3778 assert_eq!(expected, nonempty![Type::entity_type(names::ANY_ENTITY_TYPE.clone())]);
3779 assert_eq!(actual, Type::Long);
3780 assert_eq!(advice, None);
3781 }
3782 );
3783 }
3784
3785 #[test]
3786 fn interpret_contains_all_and_contains_any() -> Result<()> {
3787 let request = basic_request();
3788 let entities = basic_entities();
3789 let eval = Evaluator::new(request, &entities, Extensions::none());
3790 assert_eq!(
3792 eval.interpret_inline_policy(&Expr::contains_all(
3793 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
3794 Expr::set(vec![Expr::val(1), Expr::val(-22)])
3795 )),
3796 Ok(Value::from(true))
3797 );
3798 assert_eq!(
3800 eval.interpret_inline_policy(&Expr::contains_all(
3801 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
3802 Expr::set(vec![Expr::val(-22), Expr::val(1)])
3803 )),
3804 Ok(Value::from(true))
3805 );
3806 assert_eq!(
3808 eval.interpret_inline_policy(&Expr::contains_all(
3809 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
3810 Expr::set(vec![Expr::val(-22)])
3811 )),
3812 Ok(Value::from(true))
3813 );
3814 assert_eq!(
3816 eval.interpret_inline_policy(&Expr::contains_all(
3817 Expr::set(vec![Expr::val(43), Expr::val(34)]),
3818 Expr::set(vec![Expr::val(34), Expr::val(43)])
3819 )),
3820 Ok(Value::from(true))
3821 );
3822 assert_eq!(
3824 eval.interpret_inline_policy(&Expr::contains_all(
3825 Expr::set(vec![Expr::val(1), Expr::val(-2), Expr::val(34)]),
3826 Expr::set(vec![Expr::val(1), Expr::val(-22)])
3827 )),
3828 Ok(Value::from(false))
3829 );
3830 assert_eq!(
3832 eval.interpret_inline_policy(&Expr::contains_all(
3833 Expr::set(vec![Expr::val(1), Expr::val(34)]),
3834 Expr::set(vec![Expr::val(1), Expr::val(101), Expr::val(34)])
3835 )),
3836 Ok(Value::from(false))
3837 );
3838 assert_eq!(
3840 eval.interpret_inline_policy(&Expr::contains_all(
3841 Expr::set(vec![Expr::val(1), Expr::val(34), Expr::val(102)]),
3842 Expr::set(vec![Expr::val(1), Expr::val(101), Expr::val(34)])
3843 )),
3844 Ok(Value::from(false))
3845 );
3846 assert_eq!(
3848 eval.interpret_inline_policy(&Expr::contains_all(
3849 Expr::set(vec![Expr::val(2), Expr::val(-7), Expr::val(387)]),
3850 Expr::set(vec![Expr::val(1), Expr::val(101), Expr::val(34)])
3851 )),
3852 Ok(Value::from(false))
3853 );
3854 assert_eq!(
3856 eval.interpret_inline_policy(&Expr::contains_all(
3857 Expr::set(vec![Expr::val(2), Expr::val(43)]),
3858 Expr::set(vec![])
3859 )),
3860 Ok(Value::from(true))
3861 );
3862 assert_eq!(
3864 eval.interpret_inline_policy(&Expr::contains_all(
3865 Expr::set(vec![]),
3866 Expr::set(vec![Expr::val(2), Expr::val(43)])
3867 )),
3868 Ok(Value::from(false))
3869 );
3870 assert_eq!(
3872 eval.interpret_inline_policy(&Expr::contains_all(
3873 Expr::set(vec![
3874 Expr::val(EntityUID::with_eid("bar")),
3875 Expr::val(EntityUID::with_eid("foo"))
3876 ]),
3877 Expr::set(vec![Expr::val(EntityUID::with_eid("foo"))])
3878 )),
3879 Ok(Value::from(true))
3880 );
3881 assert_eq!(
3883 eval.interpret_inline_policy(&Expr::contains_all(
3884 Expr::set(vec![
3885 Expr::val(false),
3886 Expr::val(3),
3887 Expr::set(vec![Expr::val(47), Expr::val(0)]),
3888 Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap()
3889 ]),
3890 Expr::set(vec![
3891 Expr::val(3),
3892 Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap()
3893 ])
3894 )),
3895 Ok(Value::from(true))
3896 );
3897 assert_matches!(
3899 eval.interpret_inline_policy(&Expr::contains_all(
3900 Expr::val("ham"),
3901 Expr::val("ham and eggs")
3902 )),
3903 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3904 assert_eq!(expected, nonempty![Type::Set]);
3905 assert_eq!(actual, Type::String);
3906 assert_eq!(advice, None);
3907 }
3908 );
3909 assert_matches!(
3911 eval.interpret_inline_policy(&Expr::contains_all(
3912 Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap(),
3913 Expr::record(vec![
3914 ("2".into(), Expr::val("ham")),
3915 ("3".into(), Expr::val("eggs"))
3916 ])
3917 .unwrap()
3918 )),
3919 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3920 assert_eq!(expected, nonempty![Type::Set]);
3921 assert_eq!(actual, Type::Record);
3922 assert_eq!(advice, None);
3923 }
3924 );
3925 assert_eq!(
3927 eval.interpret_inline_policy(&Expr::contains_any(
3928 Expr::set(vec![Expr::val(1), Expr::val(-22)]),
3929 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)])
3930 )),
3931 Ok(Value::from(true))
3932 );
3933 assert_eq!(
3935 eval.interpret_inline_policy(&Expr::contains_any(
3936 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
3937 Expr::set(vec![Expr::val(1), Expr::val(-22)])
3938 )),
3939 Ok(Value::from(true))
3940 );
3941 assert_eq!(
3943 eval.interpret_inline_policy(&Expr::contains_any(
3944 Expr::set(vec![Expr::val(-22)]),
3945 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)])
3946 )),
3947 Ok(Value::from(true))
3948 );
3949 assert_eq!(
3951 eval.interpret_inline_policy(&Expr::contains_any(
3952 Expr::set(vec![Expr::val(1), Expr::val(101)]),
3953 Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)])
3954 )),
3955 Ok(Value::from(true))
3956 );
3957 assert_eq!(
3959 eval.interpret_inline_policy(&Expr::contains_any(
3960 Expr::set(vec![Expr::val(1), Expr::val(101)]),
3961 Expr::set(vec![Expr::val(-22), Expr::val(34)])
3962 )),
3963 Ok(Value::from(false))
3964 );
3965 assert_eq!(
3967 eval.interpret_inline_policy(&Expr::contains_any(
3968 Expr::set(vec![]),
3969 Expr::set(vec![Expr::val(-22), Expr::val(34)])
3970 )),
3971 Ok(Value::from(false))
3972 );
3973 assert_eq!(
3975 eval.interpret_inline_policy(&Expr::contains_any(
3976 Expr::set(vec![Expr::val(-22), Expr::val(34)]),
3977 Expr::set(vec![])
3978 )),
3979 Ok(Value::from(false))
3980 );
3981 assert_eq!(
3983 eval.interpret_inline_policy(&Expr::contains_any(
3984 Expr::set(vec![
3985 Expr::val(EntityUID::with_eid("foo")),
3986 Expr::val(EntityUID::with_eid("bar"))
3987 ]),
3988 Expr::set(vec![
3989 Expr::val(EntityUID::with_eid("ham")),
3990 Expr::val(EntityUID::with_eid("eggs"))
3991 ])
3992 )),
3993 Ok(Value::from(false))
3994 );
3995 assert_eq!(
3997 eval.interpret_inline_policy(&Expr::contains_any(
3998 Expr::set(vec![
3999 Expr::val(3),
4000 Expr::record(vec![
4001 ("2".into(), Expr::val("ham")),
4002 ("1".into(), Expr::val("eggs"))
4003 ])
4004 .unwrap()
4005 ]),
4006 Expr::set(vec![
4007 Expr::val(7),
4008 Expr::val(false),
4009 Expr::set(vec![Expr::val(-22), Expr::val(true)]),
4010 Expr::record(vec![
4011 ("1".into(), Expr::val("eggs")),
4012 ("2".into(), Expr::val("ham"))
4013 ])
4014 .unwrap()
4015 ])
4016 )),
4017 Ok(Value::from(true))
4018 );
4019 assert_matches!(
4021 eval.interpret_inline_policy(&Expr::contains_any(
4022 Expr::val("ham"),
4023 Expr::val("ham and eggs")
4024 )),
4025 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4026 assert_eq!(expected, nonempty![Type::Set]);
4027 assert_eq!(actual, Type::String);
4028 assert_eq!(advice, None);
4029 }
4030 );
4031 assert_matches!(
4033 eval.interpret_inline_policy(&Expr::contains_any(
4034 Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap(),
4035 Expr::record(vec![
4036 ("2".into(), Expr::val("ham")),
4037 ("3".into(), Expr::val("eggs"))
4038 ])
4039 .unwrap()
4040 )),
4041 Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4042 assert_eq!(expected, nonempty![Type::Set]);
4043 assert_eq!(actual, Type::Record);
4044 assert_eq!(advice, None);
4045 }
4046 );
4047 Ok(())
4048 }
4049
4050 #[test]
4051 fn eval_and_or() -> Result<()> {
4052 use crate::parser;
4053 let request = basic_request();
4054 let eparser: EntityJsonParser<'_, '_> =
4055 EntityJsonParser::new(None, Extensions::none(), TCComputation::ComputeNow);
4056 let entities = eparser.from_json_str("[]").expect("empty slice");
4057 let evaluator = Evaluator::new(request, &entities, Extensions::none());
4058
4059 let raw_expr = "(false && 3)";
4061 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4062 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4063
4064 let raw_expr = "(true || 3)";
4065 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4066 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4067
4068 let raw_expr = "(false && 3) == 3";
4070 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4071 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4072
4073 let raw_expr = "(true || 3) == 3";
4074 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4075 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4076
4077 let raw_expr = "(false && 3 && true) == 3";
4078 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4079 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4080
4081 let raw_expr = "(true || 3 || true) == 3";
4082 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4083 assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4084
4085 let raw_expr = "(true && 3)";
4087 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4088 let t = evaluator.interpret_inline_policy(&expr);
4089 println!("EXPR={:?}", t);
4090 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4091
4092 let raw_expr = "(3 && true)";
4093 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4094 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4095
4096 let raw_expr = "(3 && false)";
4097 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4098 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4099
4100 let raw_expr = "(3 || true)";
4101 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4102 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4103
4104 let raw_expr = "(3 || false)";
4105 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4106 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4107
4108 let raw_expr = "(false || 3)";
4109 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4110 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4111
4112 let raw_expr = "(true && 3) == 3";
4113 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4114 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4115
4116 let raw_expr = "(3 && true) == 3";
4117 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4118 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4119
4120 let raw_expr = "(3 && false) == 3";
4121 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4122 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4123
4124 let raw_expr = "(3 || true) == 3";
4125 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4126 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4127
4128 let raw_expr = "(3 || false) == 3";
4129 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4130 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4131
4132 let raw_expr = "(false || 3) == 3";
4133 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4134 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4135
4136 let raw_expr = "(true && 3 && true) == 3";
4137 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4138 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4139
4140 let raw_expr = "(3 && true && true) == 3";
4141 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4142 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4143
4144 let raw_expr = "(3 && false && true) == 3";
4145 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4146 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4147
4148 let raw_expr = "(3 || true || true) == 3";
4149 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4150 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4151
4152 let raw_expr = "(3 || false || true) == 3";
4153 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4154 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4155
4156 let raw_expr = "(false || 3 || true) == 3";
4157 let expr = parser::parse_expr(raw_expr).expect("parse fail");
4158 assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4159
4160 Ok(())
4161 }
4162
4163 #[test]
4164 fn template_env_tests() {
4165 let request = basic_request();
4166 let eparser: EntityJsonParser<'_, '_> =
4167 EntityJsonParser::new(None, Extensions::none(), TCComputation::ComputeNow);
4168 let entities = eparser.from_json_str("[]").expect("empty slice");
4169 let evaluator = Evaluator::new(request, &entities, Extensions::none());
4170 let e = Expr::slot(SlotId::principal());
4171
4172 let slots = HashMap::new();
4173 let r = evaluator.partial_interpret(&e, &slots);
4174 assert_matches!(r, Err(EvaluationError::UnlinkedSlot(UnlinkedSlotError { slot, .. })) => {
4175 assert_eq!(slot, SlotId::principal());
4176 });
4177
4178 let mut slots = HashMap::new();
4179 slots.insert(SlotId::principal(), EntityUID::with_eid("eid"));
4180 let r = evaluator.partial_interpret(&e, &slots);
4181 assert_matches!(r, Ok(e) => {
4182 assert_eq!(
4183 e,
4184 PartialValue::Value(Value::from(
4185 EntityUID::with_eid("eid")
4186 ))
4187 );
4188 });
4189 }
4190
4191 #[test]
4192 fn template_interp() {
4193 let t = parse_policy_or_template(
4194 Some(PolicyID::from_string("template")),
4195 r#"permit(principal == ?principal, action, resource);"#,
4196 )
4197 .expect("Parse Error");
4198 let mut pset = PolicySet::new();
4199 pset.add_template(t)
4200 .expect("Template already present in PolicySet");
4201 let mut values = HashMap::new();
4202 values.insert(SlotId::principal(), EntityUID::with_eid("p"));
4203 pset.link(
4204 PolicyID::from_string("template"),
4205 PolicyID::from_string("instance"),
4206 values,
4207 )
4208 .expect("Linking failed!");
4209 let q = Request::new(
4210 (EntityUID::with_eid("p"), None),
4211 (EntityUID::with_eid("a"), None),
4212 (EntityUID::with_eid("r"), None),
4213 Context::empty(),
4214 Some(&RequestSchemaAllPass),
4215 Extensions::none(),
4216 )
4217 .unwrap();
4218 let eparser: EntityJsonParser<'_, '_> =
4219 EntityJsonParser::new(None, Extensions::none(), TCComputation::ComputeNow);
4220 let entities = eparser.from_json_str("[]").expect("empty slice");
4221 let eval = Evaluator::new(q, &entities, Extensions::none());
4222
4223 let ir = pset.policies().next().expect("No linked policies");
4224 assert_matches!(eval.partial_evaluate(ir), Ok(Either::Left(b)) => {
4225 assert!(b, "Should be enforced");
4226 });
4227 }
4228
4229 #[track_caller] fn assert_restricted_expression_error(e: Expr) {
4231 assert_matches!(
4232 BorrowedRestrictedExpr::new(&e),
4233 Err(RestrictedExpressionError::InvalidRestrictedExpression { .. })
4234 );
4235 }
4236
4237 #[test]
4238 fn restricted_expressions() {
4239 let evaluator = RestrictedEvaluator::new(Extensions::all_available());
4240
4241 assert_eq!(
4243 evaluator.partial_interpret(BorrowedRestrictedExpr::new(&Expr::val(true)).unwrap()),
4244 Ok(Value::from(true).into())
4245 );
4246 assert_eq!(
4247 evaluator.partial_interpret(BorrowedRestrictedExpr::new(&Expr::val(-2)).unwrap()),
4248 Ok(Value::from(-2).into())
4249 );
4250 assert_eq!(
4251 evaluator
4252 .partial_interpret(BorrowedRestrictedExpr::new(&Expr::val("hello world")).unwrap()),
4253 Ok(Value::from("hello world").into())
4254 );
4255 assert_eq!(
4256 evaluator.partial_interpret(
4257 BorrowedRestrictedExpr::new(&Expr::val(EntityUID::with_eid("alice"))).unwrap()
4258 ),
4259 Ok(Value::from(EntityUID::with_eid("alice")).into())
4260 );
4261 assert_restricted_expression_error(Expr::var(Var::Principal));
4262 assert_restricted_expression_error(Expr::var(Var::Action));
4263 assert_restricted_expression_error(Expr::var(Var::Resource));
4264 assert_restricted_expression_error(Expr::var(Var::Context));
4265 assert_restricted_expression_error(Expr::ite(Expr::val(true), Expr::val(7), Expr::val(12)));
4266 assert_restricted_expression_error(Expr::and(Expr::val("bogus"), Expr::val(true)));
4267 assert_restricted_expression_error(Expr::or(Expr::val("bogus"), Expr::val(true)));
4268 assert_restricted_expression_error(Expr::not(Expr::val(true)));
4269 assert_restricted_expression_error(Expr::is_in(
4270 Expr::val(EntityUID::with_eid("alice")),
4271 Expr::val(EntityUID::with_eid("some_group")),
4272 ));
4273 assert_restricted_expression_error(Expr::is_eq(
4274 Expr::val(EntityUID::with_eid("alice")),
4275 Expr::val(EntityUID::with_eid("some_group")),
4276 ));
4277 #[cfg(feature = "ipaddr")]
4278 assert_matches!(
4279 evaluator.partial_interpret(
4280 BorrowedRestrictedExpr::new(&Expr::call_extension_fn(
4281 "ip".parse().expect("should be a valid Name"),
4282 vec![Expr::val("222.222.222.222")]
4283 ))
4284 .unwrap()
4285 ),
4286 Ok(PartialValue::Value(Value {
4287 value: ValueKind::ExtensionValue(_),
4288 ..
4289 }))
4290 );
4291 assert_restricted_expression_error(Expr::get_attr(
4292 Expr::val(EntityUID::with_eid("alice")),
4293 "pancakes".into(),
4294 ));
4295 assert_restricted_expression_error(Expr::has_attr(
4296 Expr::val(EntityUID::with_eid("alice")),
4297 "pancakes".into(),
4298 ));
4299 assert_restricted_expression_error(Expr::like(
4300 Expr::val("abcdefg12"),
4301 vec![
4302 PatternElem::Char('a'),
4303 PatternElem::Char('b'),
4304 PatternElem::Char('c'),
4305 PatternElem::Wildcard,
4306 ],
4307 ));
4308 assert_matches!(
4309 evaluator.partial_interpret(
4310 BorrowedRestrictedExpr::new(&Expr::set([Expr::val("hi"), Expr::val("there")]))
4311 .unwrap()
4312 ),
4313 Ok(PartialValue::Value(Value {
4314 value: ValueKind::Set(_),
4315 ..
4316 }))
4317 );
4318 assert_matches!(
4319 evaluator.partial_interpret(
4320 BorrowedRestrictedExpr::new(
4321 &Expr::record([
4322 ("hi".into(), Expr::val(1001)),
4323 ("foo".into(), Expr::val("bar"))
4324 ])
4325 .unwrap()
4326 )
4327 .unwrap()
4328 ),
4329 Ok(PartialValue::Value(Value {
4330 value: ValueKind::Record(_),
4331 ..
4332 }))
4333 );
4334
4335 assert_restricted_expression_error(Expr::set([
4337 Expr::val("hi"),
4338 Expr::and(Expr::val("bogus"), Expr::val(false)),
4339 ]));
4340 assert_restricted_expression_error(Expr::call_extension_fn(
4341 "ip".parse().expect("should be a valid Name"),
4342 vec![Expr::var(Var::Principal)],
4343 ));
4344
4345 assert_restricted_expression_error(Expr::is_entity_type(
4346 Expr::val(EntityUID::with_eid("alice")),
4347 "User".parse().unwrap(),
4348 ));
4349 }
4350
4351 #[test]
4352 fn simple_partial() {
4353 let pset = parse_policyset(
4354 r#"
4355 permit(principal == Principal::"alice", action, resource);
4356 "#,
4357 )
4358 .expect("Failed to parse");
4359 let euid =
4360 Arc::new(EntityUID::from_str(r#"Principal::"alice""#).expect("EUID failed to parse"));
4361 let p = pset
4362 .get(&PolicyID::from_string("policy0"))
4363 .expect("No such policy");
4364 let q = Request::new_with_unknowns(
4365 EntityUIDEntry::Unknown { loc: None },
4366 EntityUIDEntry::Unknown { loc: None },
4367 EntityUIDEntry::Unknown { loc: None },
4368 Some(Context::empty()),
4369 Some(&RequestSchemaAllPass),
4370 Extensions::none(),
4371 )
4372 .unwrap();
4373 let es = Entities::new();
4374 let e = Evaluator::new(q, &es, Extensions::none());
4375 match e.partial_evaluate(p).expect("eval error") {
4376 Either::Left(_) => panic!("Evalled to a value"),
4377 Either::Right(expr) => {
4378 println!("{expr}");
4379 assert!(expr.contains_unknown());
4380 let m: HashMap<_, _> = [("principal".into(), Value::from(euid))]
4381 .into_iter()
4382 .collect();
4383 let new_expr = expr.substitute_typed(&m).unwrap();
4384 assert_eq!(
4385 e.partial_interpret(&new_expr, &HashMap::new())
4386 .expect("Failed to eval"),
4387 PartialValue::Value(true.into())
4388 );
4389 }
4390 }
4391 }
4392
4393 fn partial_context_test(context_expr: Expr, e: Expr) -> Either<Value, Expr> {
4394 let euid: EntityUID = r#"Test::"test""#.parse().unwrap();
4395 let rexpr = RestrictedExpr::new(context_expr)
4396 .expect("Context Expression was not a restricted expression");
4397 let context = Context::from_expr(rexpr.as_borrowed(), Extensions::none()).unwrap();
4398 let q = Request::new(
4399 (euid.clone(), None),
4400 (euid.clone(), None),
4401 (euid, None),
4402 context,
4403 Some(&RequestSchemaAllPass),
4404 Extensions::none(),
4405 )
4406 .unwrap();
4407 let es = Entities::new();
4408 let eval = Evaluator::new(q, &es, Extensions::none());
4409 eval.partial_eval_expr(&e).unwrap()
4410 }
4411
4412 #[test]
4413 fn partial_contexts1() {
4414 let c_expr =
4416 Expr::record([("cell".into(), Expr::unknown(Unknown::new_untyped("cell")))]).unwrap();
4417 let expr = Expr::binary_app(
4418 BinaryOp::Eq,
4419 Expr::get_attr(Expr::var(Var::Context), "cell".into()),
4420 Expr::val(2),
4421 );
4422 let expected = Expr::binary_app(
4423 BinaryOp::Eq,
4424 Expr::unknown(Unknown::new_untyped("cell")),
4425 Expr::val(2),
4426 );
4427
4428 let r = partial_context_test(c_expr, expr);
4429
4430 assert_eq!(r, Either::Right(expected));
4431 }
4432
4433 #[test]
4434 fn partial_contexts2() {
4435 let c_expr = Expr::record([
4437 ("loc".into(), Expr::val("test")),
4438 ("cell".into(), Expr::unknown(Unknown::new_untyped("cell"))),
4439 ])
4440 .unwrap();
4441 let expr = Expr::binary_app(
4443 BinaryOp::Eq,
4444 Expr::get_attr(Expr::var(Var::Context), "cell".into()),
4445 Expr::val(2),
4446 );
4447 let r = partial_context_test(c_expr.clone(), expr);
4448 let expected = Expr::binary_app(
4449 BinaryOp::Eq,
4450 Expr::unknown(Unknown::new_untyped("cell")),
4451 Expr::val(2),
4452 );
4453 assert_eq!(r, Either::Right(expected));
4454
4455 let expr = Expr::binary_app(
4457 BinaryOp::Eq,
4458 Expr::get_attr(Expr::var(Var::Context), "loc".into()),
4459 Expr::val(2),
4460 );
4461 let r = partial_context_test(c_expr, expr);
4462 assert_eq!(r, Either::Left(false.into()));
4463 }
4464
4465 #[test]
4466 fn partial_contexts3() {
4467 let row =
4469 Expr::record([("row".into(), Expr::unknown(Unknown::new_untyped("row")))]).unwrap();
4470 let c_expr =
4472 Expr::record([("loc".into(), Expr::val("test")), ("cell".into(), row)]).unwrap();
4473 let expr = Expr::binary_app(
4476 BinaryOp::Eq,
4477 Expr::get_attr(
4478 Expr::get_attr(Expr::var(Var::Context), "cell".into()),
4479 "row".into(),
4480 ),
4481 Expr::val(2),
4482 );
4483 let r = partial_context_test(c_expr, expr);
4484 let expected = Expr::binary_app(
4485 BinaryOp::Eq,
4486 Expr::unknown(Unknown::new_untyped("row")),
4487 Expr::val(2),
4488 );
4489 assert_eq!(r, Either::Right(expected));
4490 }
4491
4492 #[test]
4493 fn partial_contexts4() {
4494 let row = Expr::record([
4496 ("row".into(), Expr::unknown(Unknown::new_untyped("row"))),
4497 ("col".into(), Expr::unknown(Unknown::new_untyped("col"))),
4498 ])
4499 .unwrap();
4500 let c_expr =
4502 Expr::record([("loc".into(), Expr::val("test")), ("cell".into(), row)]).unwrap();
4503 let expr = Expr::binary_app(
4506 BinaryOp::Eq,
4507 Expr::get_attr(
4508 Expr::get_attr(Expr::var(Var::Context), "cell".into()),
4509 "row".into(),
4510 ),
4511 Expr::val(2),
4512 );
4513 let r = partial_context_test(c_expr.clone(), expr);
4514 let expected = Expr::binary_app(
4515 BinaryOp::Eq,
4516 Expr::unknown(Unknown::new_untyped("row")),
4517 Expr::val(2),
4518 );
4519 assert_eq!(r, Either::Right(expected));
4520 let expr = Expr::binary_app(
4522 BinaryOp::Eq,
4523 Expr::get_attr(
4524 Expr::get_attr(Expr::var(Var::Context), "cell".into()),
4525 "col".into(),
4526 ),
4527 Expr::val(2),
4528 );
4529 let r = partial_context_test(c_expr, expr);
4530 let expected = Expr::binary_app(
4531 BinaryOp::Eq,
4532 Expr::unknown(Unknown::new_untyped("col")),
4533 Expr::val(2),
4534 );
4535 assert_eq!(r, Either::Right(expected));
4536 }
4537
4538 #[test]
4539 fn partial_context_fail() {
4540 let context = Context::from_expr(
4541 RestrictedExpr::new_unchecked(
4542 Expr::record([
4543 ("a".into(), Expr::val(3)),
4544 ("b".into(), Expr::unknown(Unknown::new_untyped("b"))),
4545 ])
4546 .unwrap(),
4547 )
4548 .as_borrowed(),
4549 Extensions::none(),
4550 )
4551 .unwrap();
4552 let euid: EntityUID = r#"Test::"test""#.parse().unwrap();
4553 let q = Request::new(
4554 (euid.clone(), None),
4555 (euid.clone(), None),
4556 (euid, None),
4557 context,
4558 Some(&RequestSchemaAllPass),
4559 Extensions::none(),
4560 )
4561 .unwrap();
4562 let es = Entities::new();
4563 let eval = Evaluator::new(q, &es, Extensions::none());
4564 let e = Expr::get_attr(Expr::var(Var::Context), "foo".into());
4565 assert_matches!(eval.partial_eval_expr(&e), Err(_))
4566 }
4567
4568 #[test]
4569 fn mikes_test() {
4570 let policyset = parse_policyset(
4571 r#"
4572 permit(
4573 principal == Principal::"p",
4574 action == Action::"a",
4575 resource == Table::"t"
4576 ) when {
4577 context.cell.row > 5 && context.cell.col < 2
4578 };
4579 "#,
4580 )
4581 .expect("Failed to parse");
4582 let policy = policyset
4583 .get(&PolicyID::from_string("policy0"))
4584 .expect("No such policy");
4585
4586 let es = Entities::new();
4587
4588 let p: EntityUID = r#"Principal::"p""#.parse().expect("Failed to parse");
4589 let a: EntityUID = r#"Action::"a""#.parse().expect("Failed to parse");
4590 let r: EntityUID = r#"Table::"t""#.parse().expect("Failed to parse");
4591
4592 let c_expr = RestrictedExpr::new(
4593 Expr::record([("cell".into(), Expr::unknown(Unknown::new_untyped("cell")))]).unwrap(),
4594 )
4595 .expect("should qualify as restricted");
4596 let context = Context::from_expr(c_expr.as_borrowed(), Extensions::none()).unwrap();
4597
4598 let q = Request::new(
4599 (p, None),
4600 (a, None),
4601 (r, None),
4602 context,
4603 Some(&RequestSchemaAllPass),
4604 Extensions::none(),
4605 )
4606 .unwrap();
4607 let eval = Evaluator::new(q, &es, Extensions::none());
4608
4609 let result = eval.partial_evaluate(policy).expect("Eval error");
4610 match result {
4611 Either::Left(_) => panic!("Got a value"),
4612 Either::Right(r) => {
4613 println!("{r}");
4614 }
4615 }
4616 }
4617
4618 fn empty_request() -> Request {
4619 let p: EntityUID = r#"p::"Principal""#.parse().unwrap();
4620 let a: EntityUID = r#"a::"Action""#.parse().unwrap();
4621 let r: EntityUID = r#"r::"Resource""#.parse().unwrap();
4622 let c = Context::empty();
4623 Request::new(
4624 (p, None),
4625 (a, None),
4626 (r, None),
4627 c,
4628 Some(&RequestSchemaAllPass),
4629 Extensions::none(),
4630 )
4631 .unwrap()
4632 }
4633
4634 #[test]
4635 fn if_semantics_residual_guard() {
4636 let a = Expr::unknown(Unknown::new_untyped("guard"));
4637 let b = Expr::and(Expr::val(1), Expr::val(2));
4638 let c = Expr::val(true);
4639
4640 let e = Expr::ite(a, b.clone(), c);
4641
4642 let es = Entities::new();
4643
4644 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4645
4646 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
4647
4648 assert_eq!(
4649 r,
4650 PartialValue::Residual(Expr::ite(
4651 Expr::unknown(Unknown::new_untyped("guard")),
4652 b,
4653 Expr::val(true)
4654 ))
4655 )
4656 }
4657
4658 #[test]
4659 fn if_semantics_residual_reduce() {
4660 let a = Expr::binary_app(
4661 BinaryOp::Eq,
4662 Expr::get_attr(Expr::var(Var::Context), "condition".into()),
4663 Expr::val("value"),
4664 );
4665 let b = Expr::val("true branch");
4666 let c = Expr::val("false branch");
4667
4668 let e = Expr::ite(a, b.clone(), c.clone());
4669
4670 let es = Entities::new();
4671
4672 let q = Request::new(
4673 (EntityUID::with_eid("p"), None),
4674 (EntityUID::with_eid("a"), None),
4675 (EntityUID::with_eid("r"), None),
4676 Context::from_expr(
4677 RestrictedExpr::new_unchecked(
4678 Expr::record([(
4679 "condition".into(),
4680 Expr::unknown(Unknown::new_untyped("unknown_condition")),
4681 )])
4682 .unwrap(),
4683 )
4684 .as_borrowed(),
4685 Extensions::none(),
4686 )
4687 .unwrap(),
4688 Some(&RequestSchemaAllPass),
4689 Extensions::none(),
4690 )
4691 .unwrap();
4692 let eval = Evaluator::new(q, &es, Extensions::none());
4693
4694 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
4695
4696 assert_eq!(
4697 r,
4698 PartialValue::Residual(Expr::ite(
4699 Expr::binary_app(
4700 BinaryOp::Eq,
4701 Expr::unknown(Unknown::new_untyped("unknown_condition")),
4702 Expr::val("value"),
4703 ),
4704 b,
4705 c
4706 ))
4707 );
4708 }
4709
4710 #[test]
4711 fn if_semantics_both_err() {
4712 let a = Expr::unknown(Unknown::new_untyped("guard"));
4713 let b = Expr::and(Expr::val(1), Expr::val(2));
4714 let c = Expr::or(Expr::val(1), Expr::val(3));
4715
4716 let e = Expr::ite(a, b.clone(), c.clone());
4717
4718 let es = Entities::new();
4719
4720 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4721
4722 assert_eq!(
4723 eval.partial_interpret(&e, &HashMap::new()).unwrap(),
4724 PartialValue::Residual(Expr::ite(
4725 Expr::unknown(Unknown::new_untyped("guard")),
4726 b,
4727 c
4728 ))
4729 );
4730 }
4731
4732 #[test]
4733 fn and_semantics1() {
4734 let e = Expr::and(
4736 Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(2)),
4737 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
4738 );
4739
4740 let es = Entities::new();
4741 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4742
4743 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
4744
4745 assert_eq!(r, PartialValue::Value(Value::from(false)));
4746 }
4747
4748 #[test]
4749 fn and_semantics2() {
4750 let e = Expr::and(
4752 Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2)),
4753 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
4754 );
4755
4756 let es = Entities::new();
4757 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4758
4759 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
4760
4761 assert_eq!(
4762 r,
4763 PartialValue::Residual(Expr::and(
4764 Expr::val(true),
4765 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false))
4766 ))
4767 );
4768 }
4769
4770 #[test]
4771 fn and_semantics3() {
4772 let e = Expr::and(
4774 Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
4775 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
4776 );
4777
4778 let es = Entities::new();
4779 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4780
4781 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
4782 }
4783
4784 #[test]
4785 fn and_semantics4() {
4786 let e = Expr::and(
4788 Expr::binary_app(
4789 BinaryOp::Eq,
4790 Expr::unknown(Unknown::new_untyped("a")),
4791 Expr::val(2),
4792 ),
4793 Expr::and(Expr::val("hello"), Expr::val("bye")),
4794 );
4795
4796 let es = Entities::new();
4797 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4798
4799 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Ok(_));
4800 }
4801
4802 #[test]
4803 fn or_semantics1() {
4804 let e = Expr::or(
4807 Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2)),
4808 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
4809 );
4810
4811 let es = Entities::new();
4812 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4813
4814 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
4815
4816 assert_eq!(r, PartialValue::Value(Value::from(true)));
4817 }
4818
4819 #[test]
4820 fn or_semantics2() {
4821 let e = Expr::or(
4823 Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(2)),
4824 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
4825 );
4826
4827 let es = Entities::new();
4828 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4829
4830 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
4831
4832 assert_eq!(
4833 r,
4834 PartialValue::Residual(Expr::or(
4835 Expr::val(false),
4836 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false))
4837 ))
4838 );
4839 }
4840
4841 #[test]
4842 fn or_semantics3() {
4843 let e = Expr::or(
4845 Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
4846 Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
4847 );
4848
4849 let es = Entities::new();
4850 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4851
4852 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
4853 }
4854
4855 #[test]
4856 fn or_semantics4() {
4857 let e = Expr::or(
4859 Expr::binary_app(
4860 BinaryOp::Eq,
4861 Expr::unknown(Unknown::new_untyped("a")),
4862 Expr::val(2),
4863 ),
4864 Expr::and(Expr::val("hello"), Expr::val("bye")),
4865 );
4866
4867 let es = Entities::new();
4868 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4869
4870 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Ok(_));
4871 }
4872
4873 #[test]
4874 fn record_semantics_err() {
4875 let a = Expr::get_attr(
4876 Expr::record([("value".into(), Expr::unknown(Unknown::new_untyped("test")))]).unwrap(),
4877 "notpresent".into(),
4878 );
4879
4880 let es = Entities::new();
4881 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4882
4883 assert_matches!(eval.partial_interpret(&a, &HashMap::new()), Err(_));
4884 }
4885
4886 #[test]
4887 fn record_semantics_key_present() {
4888 let a = Expr::get_attr(
4889 Expr::record([("value".into(), Expr::unknown(Unknown::new_untyped("test")))]).unwrap(),
4890 "value".into(),
4891 );
4892
4893 let es = Entities::new();
4894 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4895
4896 let r = eval.partial_interpret(&a, &HashMap::new()).unwrap();
4897
4898 let expected = PartialValue::unknown(Unknown::new_untyped("test"));
4899
4900 assert_eq!(r, expected);
4901 }
4902
4903 #[test]
4904 fn record_semantics_missing_attr() {
4905 let a = Expr::get_attr(
4906 Expr::record([
4907 ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
4908 ("b".into(), Expr::unknown(Unknown::new_untyped("c"))),
4909 ])
4910 .unwrap(),
4911 "c".into(),
4912 );
4913
4914 let es = Entities::new();
4915 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4916
4917 assert_matches!(eval.partial_interpret(&a, &HashMap::new()), Err(_));
4918 }
4919
4920 #[test]
4921 fn record_semantics_mult_unknowns() {
4922 let a = Expr::get_attr(
4923 Expr::record([
4924 ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
4925 ("b".into(), Expr::unknown(Unknown::new_untyped("b"))),
4926 ])
4927 .unwrap(),
4928 "b".into(),
4929 );
4930
4931 let es = Entities::new();
4932 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4933
4934 let r = eval.partial_interpret(&a, &HashMap::new()).unwrap();
4935
4936 let expected = PartialValue::unknown(Unknown::new_untyped("b"));
4937
4938 assert_eq!(r, expected);
4939 }
4940
4941 #[test]
4942 fn parital_if_noerrors() {
4943 let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
4944 let cons = Expr::val(1);
4945 let alt = Expr::val(2);
4946 let e = Expr::ite(guard.clone(), cons, alt);
4947
4948 let es = Entities::new();
4949 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4950
4951 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
4952
4953 let expected = Expr::ite(guard, Expr::val(1), Expr::val(2));
4954
4955 assert_eq!(r, PartialValue::Residual(expected));
4956 }
4957
4958 #[test]
4959 fn parital_if_cons_error() {
4960 let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
4961 let cons = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(true));
4962 let alt = Expr::val(2);
4963 let e = Expr::ite(guard.clone(), cons.clone(), alt);
4964
4965 let es = Entities::new();
4966 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4967
4968 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
4969
4970 let expected = Expr::ite(guard, cons, Expr::val(2));
4971
4972 assert_eq!(r, PartialValue::Residual(expected));
4973 }
4974
4975 #[test]
4976 fn parital_if_alt_error() {
4977 let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
4978 let cons = Expr::val(2);
4979 let alt = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(true));
4980 let e = Expr::ite(guard.clone(), cons, alt.clone());
4981
4982 let es = Entities::new();
4983 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
4984
4985 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
4986
4987 let expected = Expr::ite(guard, Expr::val(2), alt);
4988 assert_eq!(r, PartialValue::Residual(expected));
4989 }
4990
4991 #[test]
4992 fn parital_if_both_error() {
4993 let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
4994 let cons = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(true));
4995 let alt = Expr::less(Expr::val("hello"), Expr::val("bye"));
4996 let e = Expr::ite(guard.clone(), cons.clone(), alt.clone());
4997
4998 let es = Entities::new();
4999 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5000
5001 assert_eq!(
5002 eval.partial_interpret(&e, &HashMap::new()).unwrap(),
5003 PartialValue::Residual(Expr::ite(guard, cons, alt))
5004 );
5005 }
5006
5007 #[test]
5009 fn partial_and_err_res() {
5010 let lhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("test"));
5011 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5012 let e = Expr::and(lhs, rhs);
5013 let es = Entities::new();
5014 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5015
5016 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5017 }
5018
5019 #[test]
5021 fn partial_or_err_res() {
5022 let lhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("test"));
5023 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5024 let e = Expr::or(lhs, rhs);
5025 let es = Entities::new();
5026 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5027
5028 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5029 }
5030
5031 #[test]
5033 fn partial_and_true_res() {
5034 let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(1));
5035 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5036 let e = Expr::and(lhs, rhs);
5037 let es = Entities::new();
5038 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5039
5040 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5041
5042 let expected = Expr::and(
5043 Expr::val(true),
5044 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5045 );
5046 assert_eq!(r, PartialValue::Residual(expected));
5047 }
5048
5049 #[test]
5051 fn partial_and_false_res() {
5052 let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5053 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5054 let e = Expr::and(lhs, rhs);
5055 let es = Entities::new();
5056 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5057
5058 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5059 assert_eq!(r, PartialValue::Value(Value::from(false)));
5060 }
5061
5062 #[test]
5064 fn partial_and_res_true() {
5065 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5066 let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2));
5067 let e = Expr::and(lhs.clone(), rhs.clone());
5068 let es = Entities::new();
5069 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5070
5071 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5072 let expected = Expr::and(lhs, rhs);
5073 assert_eq!(r, PartialValue::Residual(expected));
5074 }
5075
5076 #[test]
5077 fn partial_and_res_false() {
5078 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5079 let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5080 let e = Expr::and(lhs.clone(), rhs.clone());
5081 let es = Entities::new();
5082 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5083
5084 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5085 let expected = Expr::and(lhs, rhs);
5086 assert_eq!(r, PartialValue::Residual(expected));
5087 }
5088
5089 #[test]
5091 fn partial_and_res_res() {
5092 let lhs = Expr::unknown(Unknown::new_untyped("b"));
5093 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5094 let e = Expr::and(lhs, rhs);
5095 let es = Entities::new();
5096 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5097
5098 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5099
5100 let expected = Expr::and(
5101 Expr::unknown(Unknown::new_untyped("b")),
5102 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5103 );
5104 assert_eq!(r, PartialValue::Residual(expected));
5105 }
5106
5107 #[test]
5109 fn partial_and_res_err() {
5110 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5111 let rhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("oops"));
5112 let e = Expr::and(lhs, rhs.clone());
5113 let es = Entities::new();
5114 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5115
5116 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5117
5118 let expected = Expr::and(
5119 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5120 rhs,
5121 );
5122 assert_eq!(r, PartialValue::Residual(expected));
5123 }
5124
5125 #[test]
5127 fn partial_or_true_res() {
5128 let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(1));
5129 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5130 let e = Expr::or(lhs, rhs);
5131 let es = Entities::new();
5132 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5133
5134 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5135 assert_eq!(r, PartialValue::Value(Value::from(true)));
5136 }
5137
5138 #[test]
5140 fn partial_or_false_res() {
5141 let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5142 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5143 let e = Expr::or(lhs, rhs);
5144 let es = Entities::new();
5145 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5146
5147 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5148 let expected = Expr::or(
5149 Expr::val(false),
5150 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5151 );
5152 assert_eq!(r, PartialValue::Residual(expected));
5153 }
5154
5155 #[test]
5157 fn partial_or_res_true() {
5158 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5159 let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2));
5160 let e = Expr::or(lhs.clone(), rhs.clone());
5161 let es = Entities::new();
5162 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5163
5164 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5165 let expected = Expr::or(lhs, rhs);
5166 assert_eq!(r, PartialValue::Residual(expected));
5167 }
5168
5169 #[test]
5170 fn partial_or_res_false() {
5171 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5172 let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5173 let e = Expr::or(lhs.clone(), rhs.clone());
5174 let es = Entities::new();
5175 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5176
5177 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5178 let expected = Expr::or(lhs, rhs);
5179 assert_eq!(r, PartialValue::Residual(expected));
5180 }
5181
5182 #[test]
5184 fn partial_or_res_res() {
5185 let lhs = Expr::unknown(Unknown::new_untyped("b"));
5186 let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5187 let e = Expr::or(lhs, rhs);
5188 let es = Entities::new();
5189 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5190
5191 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5192
5193 let expected = Expr::or(
5194 Expr::unknown(Unknown::new_untyped("b")),
5195 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5196 );
5197 assert_eq!(r, PartialValue::Residual(expected));
5198 }
5199
5200 #[test]
5202 fn partial_or_res_err() {
5203 let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5204 let rhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("oops"));
5205 let e = Expr::or(lhs, rhs.clone());
5206 let es = Entities::new();
5207 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5208
5209 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5210
5211 let expected = Expr::or(
5212 Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5213 rhs,
5214 );
5215 assert_eq!(r, PartialValue::Residual(expected));
5216 }
5217
5218 #[test]
5219 fn partial_unop() {
5220 let es = Entities::new();
5221 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5222
5223 let e = Expr::unary_app(UnaryOp::Neg, Expr::unknown(Unknown::new_untyped("a")));
5224 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5225 assert_eq!(r, PartialValue::Residual(e));
5226
5227 let e = Expr::unary_app(UnaryOp::Not, Expr::unknown(Unknown::new_untyped("a")));
5228 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5229 assert_eq!(r, PartialValue::Residual(e));
5230 }
5231
5232 #[test]
5233 fn partial_binop() {
5234 let es = Entities::new();
5235 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5236
5237 let binops = [
5238 BinaryOp::Add,
5239 BinaryOp::Contains,
5240 BinaryOp::ContainsAll,
5241 BinaryOp::ContainsAny,
5242 BinaryOp::Eq,
5243 BinaryOp::In,
5244 BinaryOp::Less,
5245 BinaryOp::LessEq,
5246 BinaryOp::Sub,
5247 ];
5248
5249 for binop in binops {
5250 let e = Expr::binary_app(
5252 binop,
5253 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
5254 Expr::unknown(Unknown::new_untyped("a")),
5255 );
5256 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5257 let expected = Expr::binary_app(
5258 binop,
5259 Expr::val(3),
5260 Expr::unknown(Unknown::new_untyped("a")),
5261 );
5262 assert_eq!(r, PartialValue::Residual(expected));
5263 let e = Expr::binary_app(
5265 binop,
5266 Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
5267 Expr::unknown(Unknown::new_untyped("a")),
5268 );
5269 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5270 let e = Expr::binary_app(
5272 binop,
5273 Expr::unknown(Unknown::new_untyped("a")),
5274 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
5275 );
5276 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5277 let expected = Expr::binary_app(
5278 binop,
5279 Expr::unknown(Unknown::new_untyped("a")),
5280 Expr::val(3),
5281 );
5282 assert_eq!(r, PartialValue::Residual(expected));
5283 let e = Expr::binary_app(
5285 binop,
5286 Expr::unknown(Unknown::new_untyped("a")),
5287 Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
5288 );
5289 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5290 let e = Expr::binary_app(
5292 binop,
5293 Expr::unknown(Unknown::new_untyped("a")),
5294 Expr::unknown(Unknown::new_untyped("b")),
5295 );
5296 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5297 let expected = Expr::binary_app(
5298 binop,
5299 Expr::unknown(Unknown::new_untyped("a")),
5300 Expr::unknown(Unknown::new_untyped("b")),
5301 );
5302 assert_eq!(r, PartialValue::Residual(expected));
5303 }
5304 }
5305
5306 #[test]
5307 fn partial_mul() {
5308 let es = Entities::new();
5309 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5310
5311 let e = Expr::mul(Expr::unknown(Unknown::new_untyped("a")), Expr::val(32));
5312 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5313 assert_eq!(r, PartialValue::Residual(e));
5314 }
5315
5316 #[test]
5317 fn partial_ext_constructors() {
5318 let es = Entities::new();
5319 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5320
5321 let e = Expr::call_extension_fn(
5322 "ip".parse().unwrap(),
5323 vec![Expr::unknown(Unknown::new_untyped("a"))],
5324 );
5325
5326 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5327
5328 assert_eq!(r, PartialValue::Residual(e));
5329 }
5330
5331 #[cfg(feature = "ipaddr")]
5332 #[test]
5333 fn partial_ext_unfold() {
5334 let es = Entities::new();
5335 let eval = Evaluator::new(empty_request(), &es, Extensions::all_available());
5336
5337 let a = Expr::call_extension_fn("ip".parse().unwrap(), vec![Expr::val("127.0.0.1")]);
5338 let b = Expr::unknown(Unknown::new_untyped("a"));
5339 let e = Expr::call_extension_fn("isInRange".parse().unwrap(), vec![a, b]);
5340
5341 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5342
5343 assert_eq!(r, PartialValue::Residual(e));
5344
5345 let b = Expr::call_extension_fn("ip".parse().unwrap(), vec![Expr::val("127.0.0.1")]);
5346 let a = Expr::unknown(Unknown::new_untyped("a"));
5347 let e = Expr::call_extension_fn("isInRange".parse().unwrap(), vec![a, b]);
5348
5349 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5350
5351 assert_eq!(r, PartialValue::Residual(e));
5352
5353 let b = Expr::call_extension_fn("ip".parse().unwrap(), vec![Expr::val("invalid")]);
5354 let a = Expr::unknown(Unknown::new_untyped("a"));
5355 let e = Expr::call_extension_fn("isInRange".parse().unwrap(), vec![a, b]);
5356
5357 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5358 }
5359
5360 #[test]
5361 fn partial_like() {
5362 let es = Entities::new();
5363 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5364
5365 let e = Expr::like(Expr::unknown(Unknown::new_untyped("a")), []);
5366
5367 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5368
5369 assert_eq!(r, PartialValue::Residual(e));
5370 }
5371
5372 #[test]
5373 fn partial_is() {
5374 let es = Entities::new();
5375 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5376
5377 let e = Expr::is_entity_type(
5378 Expr::unknown(Unknown::new_untyped("a")),
5379 "User".parse().unwrap(),
5380 );
5381
5382 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5383
5384 assert_eq!(r, PartialValue::Residual(e));
5385 }
5386
5387 #[test]
5388 fn partial_hasattr() {
5389 let es = Entities::new();
5390 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5391
5392 let e = Expr::has_attr(Expr::unknown(Unknown::new_untyped("a")), "test".into());
5393
5394 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5395
5396 assert_eq!(r, PartialValue::Residual(e));
5397 }
5398
5399 #[test]
5400 fn partial_set() {
5401 let es = Entities::new();
5402 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5403
5404 let e = Expr::set([
5405 Expr::val(1),
5406 Expr::unknown(Unknown::new_untyped("a")),
5407 Expr::val(2),
5408 ]);
5409 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5410 assert_eq!(r, PartialValue::Residual(e));
5411
5412 let e = Expr::set([
5413 Expr::val(1),
5414 Expr::unknown(Unknown::new_untyped("a")),
5415 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
5416 ]);
5417 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5418 assert_eq!(
5419 r,
5420 PartialValue::Residual(Expr::set([
5421 Expr::val(1),
5422 Expr::unknown(Unknown::new_untyped("a")),
5423 Expr::val(3)
5424 ]))
5425 );
5426
5427 let e = Expr::set([
5428 Expr::val(1),
5429 Expr::unknown(Unknown::new_untyped("a")),
5430 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("a")),
5431 ]);
5432 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5433 }
5434
5435 #[test]
5436 fn partial_record() {
5437 let es = Entities::new();
5438 let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5439
5440 let e = Expr::record([
5441 ("a".into(), Expr::val(1)),
5442 ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
5443 ("c".into(), Expr::val(2)),
5444 ])
5445 .unwrap();
5446 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5447 assert_eq!(r, PartialValue::Residual(e));
5448
5449 let e = Expr::record([
5450 ("a".into(), Expr::val(1)),
5451 ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
5452 ]);
5453 assert_eq!(
5454 e,
5455 Err(expression_construction_errors::DuplicateKeyError {
5456 key: "a".into(),
5457 context: "in record literal",
5458 }
5459 .into())
5460 );
5461
5462 let e = Expr::record([
5463 ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
5464 ("a".into(), Expr::val(1)),
5465 ]);
5466 assert_eq!(
5467 e,
5468 Err(expression_construction_errors::DuplicateKeyError {
5469 key: "a".into(),
5470 context: "in record literal",
5471 }
5472 .into())
5473 );
5474
5475 let e = Expr::record([
5476 ("a".into(), Expr::val(1)),
5477 ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
5478 (
5479 "c".into(),
5480 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
5481 ),
5482 ])
5483 .unwrap();
5484 let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5485 assert_eq!(
5486 r,
5487 PartialValue::Residual(
5488 Expr::record([
5489 ("a".into(), Expr::val(1)),
5490 ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
5491 ("c".into(), Expr::val(3))
5492 ])
5493 .unwrap()
5494 )
5495 );
5496
5497 let e = Expr::record([
5498 ("a".into(), Expr::val(1)),
5499 ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
5500 (
5501 "c".into(),
5502 Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("hello")),
5503 ),
5504 ])
5505 .unwrap();
5506 assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5507 }
5508
5509 #[test]
5510 fn small() {
5511 let e = parser::parse_expr("[[1]]").unwrap();
5512 let re = RestrictedExpr::new(e).unwrap();
5513 let eval = RestrictedEvaluator::new(Extensions::none());
5514 let r = eval.partial_interpret(re.as_borrowed()).unwrap();
5515 assert_matches!(r, PartialValue::Value(Value { value: ValueKind::Set(set), .. }) => {
5516 assert_eq!(set.len(), 1);
5517 });
5518 }
5519
5520 #[test]
5521 fn unprojectable_residual() {
5522 let q = basic_request();
5523 let entities = basic_entities();
5524 let eval = Evaluator::new(q, &entities, Extensions::none());
5525
5526 let e = Expr::get_attr(
5527 Expr::record([
5528 (
5529 "a".into(),
5530 Expr::binary_app(
5531 BinaryOp::Add,
5532 Expr::unknown(Unknown::new_untyped("a")),
5533 Expr::val(3),
5534 ),
5535 ),
5536 ("b".into(), Expr::val(83)),
5537 ])
5538 .unwrap(),
5539 "b".into(),
5540 );
5541 let r = eval.partial_eval_expr(&e).unwrap();
5542 assert_eq!(r, Either::Right(e));
5543
5544 let e = Expr::get_attr(
5545 Expr::record([(
5546 "a".into(),
5547 Expr::binary_app(
5548 BinaryOp::Add,
5549 Expr::unknown(Unknown::new_untyped("a")),
5550 Expr::val(3),
5551 ),
5552 )])
5553 .unwrap(),
5554 "b".into(),
5555 );
5556 assert_matches!(eval.partial_eval_expr(&e), Err(_));
5557 }
5558}