compose_library/foundations/
support.rs

1use compose_error_codes::E0012_PREDICATE_MUST_RETURN_BOOLEAN;
2use compose_library::diag::{SourceResult, bail, error};
3use compose_library::{Args, Func, Value, Vm};
4use std::iter;
5
6pub fn eval_predicate(
7    vm: &mut dyn Vm,
8    predicate: &Func,
9    value: Value,
10    callee: &str,
11) -> SourceResult<bool> {
12    let span = predicate.span;
13    let args = Args::new(span, iter::once(value));
14    match predicate.call(vm, args)? {
15        Value::Bool(b) => Ok(b),
16        other => {
17            let mut err = error!(
18                span, "predicate must return a boolean";
19                label_message: "this function should return a boolean, but returned type `{}` instead", other.ty();
20                note: "a predicate function passed to `{callee}` must return either `true` or `false`";
21                code: &E0012_PREDICATE_MUST_RETURN_BOOLEAN;
22            );
23
24            if let Some(hint) = predicate_hint(&other) {
25                err.hint(hint);
26            }
27
28            bail!(err);
29        }
30    }
31}
32
33pub fn eval_func(vm: &mut dyn Vm, func: &Func, args: impl IntoIterator<Item = Value>) -> SourceResult<Value> {
34    func.call(vm, Args::new(func.span, args))
35}
36
37fn predicate_hint(value: &Value) -> Option<&'static str> {
38    match value {
39        Value::Int(_) => Some("did you mean to write a comparison like `x == 1` or `x > 1`?"),
40        Value::Str(_) => Some("did you mean to compare it, like `x == \"hello\"`?"),
41        Value::Array(_) => {
42            Some("did you mean to check the contents, e.g., `x.contains(...)` or `x.len() > 0`?")
43        }
44        _ => None,
45    }
46}