1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
45use crate::RemoteSettingsContext;
6use firefox_versioning::compare::version_compare;
7use jexl_eval::Evaluator;
8use serde_json::{json, Value};
9use thiserror::Error;
1011#[derive(Error, Debug)]
12pub(crate) enum ParseError {
13#[error("Evaluation error: {0}")]
14EvaluationError(String),
15#[error("Invalid result type")]
16InvalidResultType,
17}
1819/// The JEXL filter is getting instatiated when a new `RemoteSettingsClient` is being created.
20pub struct JexlFilter {
21/// a JEXL `Evaluator` to run transforms and evaluations on.
22evaluator: Evaluator<'static>,
23/// The transformed `RemoteSettingsContext` as a `serde_json::Value`
24context: Value,
25}
2627impl JexlFilter {
28/// Creating a new `JEXL` filter. If no `context` is set, all future `records` are being
29 /// evaluated as `true` by default.
30pub(crate) fn new(context: Option<RemoteSettingsContext>) -> Self {
31let env_context = match context {
32Some(ctx) => json!({ "env": ctx.into_env() }),
33None => json!({ "env": {} }),
34 };
3536Self {
37 evaluator: Evaluator::new()
38 .with_transform("versionCompare", |args| Ok(version_compare(args)?)),
39 context: env_context,
40 }
41 }
4243/// Evaluates the given filter expression in the provided context.
44 /// Returns `Ok(true)` if the expression evaluates to true, `Ok(false)` otherwise.
45pub(crate) fn evaluate(&self, filter_expr: &str) -> Result<bool, ParseError> {
46if filter_expr.trim().is_empty() {
47return Ok(true);
48 }
4950let result = self
51.evaluator
52 .eval_in_context(filter_expr, &self.context)
53 .map_err(|e| {
54 ParseError::EvaluationError(format!("Failed to evaluate '{}': {}", filter_expr, e))
55 })?;
5657 result.as_bool().ok_or(ParseError::InvalidResultType)
58 }
59}