use serde_json::Value;
pub const SCOPING: &[&str] = &[
"map", "filter", "reduce", "all", "some", "none", "group_by", "distinct", "sort", "try",
"switch", "match",
];
pub const NON_SCOPING: &[&str] = &[
"var",
"val",
"==",
"!=",
"===",
"!==",
">",
">=",
"<",
"<=",
"and",
"or",
"!",
"!!",
"if",
"?:",
"+",
"-",
"*",
"/",
"%",
"max",
"min",
"cat",
"substr",
"in",
"merge",
"missing",
"missing_some",
"now",
"datetime",
"parse_date",
"format_date",
"date_diff",
"timestamp",
"length",
"upper",
"lower",
"trim",
"split",
"starts_with",
"ends_with",
"slice",
"abs",
"ceil",
"floor",
"keys",
"values",
"entries",
"??",
"type",
"exists",
"throw",
"base64_encode",
"base64_decode",
"base64url_encode",
"base64url_decode",
"hex_encode",
"hex_decode",
"random",
"url_encode",
"url_decode",
"join",
"secret",
];
pub const NONDETERMINISTIC: &[&str] = &["now", "random", "secret"];
pub fn is_scoping(op: &str) -> bool {
SCOPING.contains(&op)
}
pub fn input_expressions<'a>(function: &str, input: &'a Value) -> Vec<(String, &'a Value)> {
let mut out = Vec::new();
let Some(map) = input.as_object() else {
return out;
};
let each = |key: &str, member: &str, out: &mut Vec<(String, &'a Value)>| {
if let Some(items) = map.get(key).and_then(Value::as_array) {
for (i, item) in items.iter().enumerate() {
if let Some(expr) = item.get(member) {
out.push((format!("{key}[{i}].{member}"), expr));
}
}
}
};
let one = |key: &str, out: &mut Vec<(String, &'a Value)>| {
if let Some(expr) = map.get(key) {
out.push((key.to_string(), expr));
}
};
match function {
"map" => each("mappings", "logic", &mut out),
"filter" => one("condition", &mut out),
"validation" | "validate" => each("rules", "logic", &mut out),
"log" => {
one("message", &mut out);
if let Some(fields) = map.get("fields").and_then(Value::as_object) {
for (name, expr) in fields {
out.push((format!("fields.{name}"), expr));
}
}
}
"channel_call" => {
one("data_logic", &mut out);
one("channel_logic", &mut out);
}
_ => {}
}
for (field, value) in map {
if crate::engine::functions::schema::is_resolvable_field(function, field)
&& !out.iter().any(|(p, _)| p == field)
{
out.push((field.clone(), value));
}
}
out
}