Files
Aether/crates/aether-ai-formats/src/provider_compat/proxy/rules.rs
2026-05-07 15:56:28 +08:00

1786 lines
61 KiB
Rust

use std::{
collections::{BTreeMap, HashSet},
sync::OnceLock,
};
use regex::{Regex, RegexBuilder};
use serde_json::{Map, Value};
const ORIGINAL_PLACEHOLDER: &str = "{{$original}}";
const ITEM_PREFIX: &str = "$item.";
const ITEM_EXACT: &str = "$item";
const CONDITION_SOURCES: &[&str] = &["body", "request_headers", "headers", "original", "current"];
const CONDITION_TYPE_VALUES: &[&str] = &["string", "number", "boolean", "array", "object", "null"];
static RANGE_RE: OnceLock<Regex> = OnceLock::new();
#[derive(Clone, Copy)]
enum ConditionHeaders<'a> {
Request(&'a http::HeaderMap),
Map(&'a BTreeMap<String, String>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum BodyPathSegment {
Key(String),
Index(isize),
WildcardSlice(Option<usize>, Option<usize>),
}
pub fn header_rules_are_locally_supported(rules: Option<&Value>) -> bool {
let Some(rules) = rules else {
return true;
};
rules.is_array()
}
pub fn header_rules_have_enabled_rules(rules: Option<&Value>) -> bool {
let Some(rules) = rules.and_then(Value::as_array) else {
return false;
};
rules
.iter()
.any(|rule| rule.as_object().is_some_and(header_rule_is_enabled))
}
pub fn apply_local_header_rules(
headers: &mut BTreeMap<String, String>,
rules: Option<&Value>,
protected_keys: &[&str],
body: &Value,
original_body: Option<&Value>,
) -> bool {
apply_local_header_rules_inner(headers, rules, protected_keys, body, original_body, None)
}
pub fn apply_local_header_rules_with_request_headers(
headers: &mut BTreeMap<String, String>,
rules: Option<&Value>,
protected_keys: &[&str],
body: &Value,
original_body: Option<&Value>,
request_headers: Option<&http::HeaderMap>,
) -> bool {
apply_local_header_rules_inner(
headers,
rules,
protected_keys,
body,
original_body,
request_headers.map(ConditionHeaders::Request),
)
}
fn apply_local_header_rules_inner(
headers: &mut BTreeMap<String, String>,
rules: Option<&Value>,
protected_keys: &[&str],
body: &Value,
original_body: Option<&Value>,
request_headers: Option<ConditionHeaders<'_>>,
) -> bool {
let Some(rules) = rules else {
return true;
};
let Some(rules) = rules.as_array() else {
return false;
};
let protected_keys: HashSet<String> = protected_keys
.iter()
.map(|value| value.trim().to_ascii_lowercase())
.collect();
for rule in rules {
let Some(rule) = rule.as_object() else {
continue;
};
if !header_rule_is_enabled(rule) {
continue;
}
if let Some(condition) = rule.get("condition").filter(|value| !value.is_null()) {
if !condition_is_locally_supported(condition) {
continue;
}
let condition_headers = request_headers.or(Some(ConditionHeaders::Map(&*headers)));
if !evaluate_local_condition(body, condition, original_body, condition_headers) {
continue;
}
}
match rule
.get("action")
.and_then(Value::as_str)
.map(str::trim)
.map(str::to_ascii_lowercase)
.as_deref()
{
Some("set") => {
let Some(key) = rule.get("key").and_then(Value::as_str).map(str::trim) else {
continue;
};
let key = key.to_ascii_lowercase();
if key.is_empty() || protected_keys.contains(&key) {
continue;
}
let value = rule
.get("value")
.map(header_rule_value_to_string)
.unwrap_or_default();
headers.insert(key, value);
}
Some("drop") => {
let Some(key) = rule.get("key").and_then(Value::as_str).map(str::trim) else {
continue;
};
let key = key.to_ascii_lowercase();
if !key.is_empty() && !protected_keys.contains(&key) {
headers.remove(&key);
}
}
Some("rename") => {
let Some(from) = rule.get("from").and_then(Value::as_str).map(str::trim) else {
continue;
};
let Some(to) = rule.get("to").and_then(Value::as_str).map(str::trim) else {
continue;
};
let from = from.to_ascii_lowercase();
let to = to.to_ascii_lowercase();
if from.is_empty()
|| to.is_empty()
|| protected_keys.contains(&from)
|| protected_keys.contains(&to)
{
continue;
}
if let Some(value) = headers.remove(&from) {
headers.insert(to, value);
}
}
_ => continue,
}
}
true
}
fn header_rule_value_to_string(value: &Value) -> String {
value
.as_str()
.map(str::to_string)
.unwrap_or_else(|| value.to_string())
}
fn header_rule_is_enabled(rule: &Map<String, Value>) -> bool {
rule.get("enabled").and_then(Value::as_bool) != Some(false)
}
pub fn body_rules_are_locally_supported(rules: Option<&Value>) -> bool {
let Some(rules) = rules else {
return true;
};
rules.is_array()
}
pub fn body_rules_have_enabled_rules(rules: Option<&Value>) -> bool {
let Some(rules) = rules.and_then(Value::as_array) else {
return false;
};
rules
.iter()
.any(|rule| rule.as_object().is_some_and(body_rule_is_enabled))
}
pub fn body_rules_handle_path(rules: Option<&Value>, path: &str) -> bool {
let Some(target_path) = parse_body_path(path) else {
return false;
};
let Some(rules) = rules.and_then(Value::as_array) else {
return false;
};
rules.iter().any(|rule| {
let Some(rule) = rule.as_object() else {
return false;
};
if !body_rule_is_enabled(rule) {
return false;
}
match rule
.get("action")
.and_then(Value::as_str)
.map(str::trim)
.map(str::to_ascii_lowercase)
.as_deref()
{
Some("set")
| Some("drop")
| Some("append")
| Some("insert")
| Some("regex_replace") => rule
.get("path")
.and_then(Value::as_str)
.and_then(parse_body_path)
.is_some_and(|candidate| path_matches(&candidate, &target_path)),
Some("rename") => {
rule.get("from")
.and_then(Value::as_str)
.and_then(parse_body_path)
.is_some_and(|candidate| path_matches(&candidate, &target_path))
|| rule
.get("to")
.and_then(Value::as_str)
.and_then(parse_body_path)
.is_some_and(|candidate| path_matches(&candidate, &target_path))
}
_ => false,
}
})
}
pub fn apply_local_body_rules(
body: &mut Value,
rules: Option<&Value>,
original_body: Option<&Value>,
) -> bool {
apply_local_body_rules_inner(body, rules, original_body, None)
}
pub fn apply_local_body_rules_with_request_headers(
body: &mut Value,
rules: Option<&Value>,
original_body: Option<&Value>,
request_headers: Option<&http::HeaderMap>,
) -> bool {
apply_local_body_rules_inner(
body,
rules,
original_body,
request_headers.map(ConditionHeaders::Request),
)
}
fn apply_local_body_rules_inner(
body: &mut Value,
rules: Option<&Value>,
original_body: Option<&Value>,
request_headers: Option<ConditionHeaders<'_>>,
) -> bool {
let Some(rules) = rules else {
return true;
};
let Some(rules) = rules.as_array() else {
return false;
};
for rule in rules {
let Some(rule) = rule.as_object() else {
continue;
};
if !body_rule_is_enabled(rule) {
continue;
}
let condition = rule.get("condition").filter(|value| !value.is_null());
let item_condition = condition.is_some_and(condition_has_item_ref);
if let Some(condition) = condition {
if !condition_is_locally_supported(condition) {
continue;
}
if !item_condition
&& !evaluate_local_condition(body, condition, original_body, request_headers)
{
continue;
}
}
match rule
.get("action")
.and_then(Value::as_str)
.map(str::trim)
.map(str::to_ascii_lowercase)
.as_deref()
{
Some("set") => {
let Some(path) = rule
.get("path")
.and_then(Value::as_str)
.and_then(parse_body_path)
else {
continue;
};
let targets = iter_wildcard_targets(
body,
&path,
WildcardTargetOptions {
condition,
item_condition,
original_body,
request_headers,
require_leaf: false,
reverse: false,
},
);
let value_template = rule.get("value").cloned().unwrap_or(Value::Null);
for target_path in targets {
let value = if contains_original_placeholder(&value_template) {
let original = get_nested_value(body, &target_path);
resolve_original_placeholder(&value_template, original.as_ref())
} else {
value_template.clone()
};
let _ = set_nested_value(body, &target_path, value);
}
}
Some("drop") => {
let Some(path) = rule
.get("path")
.and_then(Value::as_str)
.and_then(parse_body_path)
else {
continue;
};
for target_path in iter_wildcard_targets(
body,
&path,
WildcardTargetOptions {
condition,
item_condition,
original_body,
request_headers,
require_leaf: true,
reverse: true,
},
) {
let _ = delete_nested_value(body, &target_path);
}
}
Some("rename") => {
let Some(from) = rule
.get("from")
.and_then(Value::as_str)
.and_then(parse_body_path)
else {
continue;
};
let Some(to) = rule
.get("to")
.and_then(Value::as_str)
.and_then(parse_body_path)
else {
continue;
};
if has_wildcard(&from) || has_wildcard(&to) {
continue;
}
let _ = rename_nested_value(body, &from, &to);
}
Some("append") => {
let Some(path) = rule
.get("path")
.and_then(Value::as_str)
.and_then(parse_body_path)
else {
continue;
};
let value = rule.get("value").cloned().unwrap_or(Value::Null);
for target_path in iter_wildcard_targets(
body,
&path,
WildcardTargetOptions {
condition,
item_condition,
original_body,
request_headers,
require_leaf: true,
reverse: false,
},
) {
if let Some(target) = get_nested_value_mut(body, &target_path) {
if let Some(values) = target.as_array_mut() {
values.push(value.clone());
}
}
}
}
Some("insert") => {
let Some(path) = rule
.get("path")
.and_then(Value::as_str)
.and_then(parse_body_path)
else {
continue;
};
let Some(index) = rule.get("index").and_then(parse_insert_index) else {
continue;
};
if has_wildcard(&path) {
continue;
}
let value = rule.get("value").cloned().unwrap_or(Value::Null);
if let Some(target) = get_nested_value_mut(body, &path) {
if let Some(values) = target.as_array_mut() {
let insert_index = normalize_insert_index(values.len(), index);
values.insert(insert_index, value);
}
}
}
Some("regex_replace") => {
let Some(path) = rule
.get("path")
.and_then(Value::as_str)
.and_then(parse_body_path)
else {
continue;
};
let Some(pattern) = rule.get("pattern").and_then(Value::as_str) else {
continue;
};
let Some(replacement) = rule.get("replacement").and_then(Value::as_str) else {
continue;
};
let flags = rule.get("flags").and_then(Value::as_str).unwrap_or("");
let count = rule
.get("count")
.and_then(parse_non_negative_count)
.unwrap_or(0);
if pattern.is_empty() {
continue;
}
let Some(pattern) = compile_regex(pattern, flags) else {
continue;
};
for target_path in iter_wildcard_targets(
body,
&path,
WildcardTargetOptions {
condition,
item_condition,
original_body,
request_headers,
require_leaf: true,
reverse: false,
},
) {
if let Some(target) = get_nested_value_mut(body, &target_path) {
let Some(current) = target.as_str().map(str::to_owned) else {
continue;
};
let replaced = if count == 0 {
pattern.replace_all(&current, replacement).to_string()
} else {
pattern.replacen(&current, count, replacement).to_string()
};
*target = Value::String(replaced);
}
}
}
_ => continue,
}
}
true
}
fn body_rule_is_enabled(rule: &Map<String, Value>) -> bool {
rule.get("enabled").and_then(Value::as_bool) != Some(false)
}
fn condition_is_locally_supported(condition: &Value) -> bool {
let Some(condition) = condition.as_object() else {
return false;
};
if let Some(children) = condition.get("all").and_then(Value::as_array) {
return !children.is_empty() && children.iter().all(condition_is_locally_supported);
}
if let Some(children) = condition.get("any").and_then(Value::as_array) {
return !children.is_empty() && children.iter().all(condition_is_locally_supported);
}
let source = condition
.get("source")
.and_then(Value::as_str)
.map(str::trim)
.unwrap_or("body");
if !CONDITION_SOURCES.contains(&source) {
return false;
}
let Some(op) = condition.get("op").and_then(Value::as_str).map(str::trim) else {
return false;
};
let Some(path) = condition.get("path").and_then(Value::as_str) else {
return false;
};
if path.trim().is_empty() {
return false;
}
if !condition_source_is_headers(source) && parse_body_path(path).is_none() {
return false;
}
match op {
"exists" | "not_exists" | "eq" | "neq" => true,
"gt" | "lt" | "gte" | "lte" => condition
.get("value")
.is_some_and(|value| value.as_f64().is_some() && !value.is_boolean()),
"starts_with" | "ends_with" | "matches" => condition
.get("value")
.and_then(Value::as_str)
.is_some_and(|value| {
if op == "matches" {
Regex::new(value).is_ok()
} else {
true
}
}),
"contains" => condition.get("value").is_some(),
"in" => condition.get("value").is_some_and(Value::is_array),
"type_is" => condition
.get("value")
.and_then(Value::as_str)
.is_some_and(|value| CONDITION_TYPE_VALUES.contains(&value)),
_ => false,
}
}
fn evaluate_local_condition(
body: &Value,
condition: &Value,
original_body: Option<&Value>,
request_headers: Option<ConditionHeaders<'_>>,
) -> bool {
let Some(condition) = condition.as_object() else {
return false;
};
if let Some(children) = condition.get("all").and_then(Value::as_array) {
return !children.is_empty()
&& children.iter().all(|child| {
evaluate_local_condition(body, child, original_body, request_headers)
});
}
if let Some(children) = condition.get("any").and_then(Value::as_array) {
return !children.is_empty()
&& children.iter().any(|child| {
evaluate_local_condition(body, child, original_body, request_headers)
});
}
let source = condition
.get("source")
.and_then(Value::as_str)
.map(str::trim)
.unwrap_or("body");
let Some(op) = condition.get("op").and_then(Value::as_str).map(str::trim) else {
return false;
};
let Some(path) = condition.get("path").and_then(Value::as_str).map(str::trim) else {
return false;
};
let current_value = if condition_source_is_headers(source) {
request_headers.and_then(|headers| get_header_condition_value(headers, path))
} else {
let target = if source.eq_ignore_ascii_case("original") {
original_body.unwrap_or(body)
} else {
body
};
parse_body_path(path).and_then(|path| get_nested_value(target, &path))
};
if op == "exists" {
return current_value.is_some();
}
if op == "not_exists" {
return current_value.is_none();
}
let Some(current_value) = current_value else {
return false;
};
let expected = condition.get("value");
match op {
"eq" => expected == Some(&current_value),
"neq" => expected != Some(&current_value),
"gt" | "lt" | "gte" | "lte" => {
let Some(current) = json_number(&current_value) else {
return false;
};
let Some(expected) = expected.and_then(json_number) else {
return false;
};
match op {
"gt" => current > expected,
"lt" => current < expected,
"gte" => current >= expected,
"lte" => current <= expected,
_ => false,
}
}
"starts_with" => current_value
.as_str()
.zip(expected.and_then(Value::as_str))
.is_some_and(|(current, expected)| current.starts_with(expected)),
"ends_with" => current_value
.as_str()
.zip(expected.and_then(Value::as_str))
.is_some_and(|(current, expected)| current.ends_with(expected)),
"contains" => match (current_value, expected) {
(Value::String(current), Some(Value::String(expected))) => current.contains(expected),
(Value::Array(current), Some(expected)) => {
current.iter().any(|value| value == expected)
}
_ => false,
},
"matches" => current_value
.as_str()
.zip(expected.and_then(Value::as_str))
.is_some_and(|(current, expected)| {
Regex::new(expected)
.map(|pattern| pattern.is_match(current))
.unwrap_or(false)
}),
"in" => expected
.and_then(Value::as_array)
.is_some_and(|values| values.iter().any(|value| value == &current_value)),
"type_is" => expected
.and_then(Value::as_str)
.is_some_and(|expected| match expected {
"string" => current_value.is_string(),
"number" => current_value.as_f64().is_some() && !current_value.is_boolean(),
"boolean" => current_value.is_boolean(),
"array" => current_value.is_array(),
"object" => current_value.is_object(),
"null" => current_value.is_null(),
_ => false,
}),
_ => false,
}
}
fn condition_source_is_headers(source: &str) -> bool {
source.eq_ignore_ascii_case("request_headers") || source.eq_ignore_ascii_case("headers")
}
fn get_header_condition_value(headers: ConditionHeaders<'_>, path: &str) -> Option<Value> {
let key = path.trim().to_ascii_lowercase();
if key.is_empty() {
return None;
}
match headers {
ConditionHeaders::Request(headers) => headers
.get(key.as_str())
.and_then(|value| value.to_str().ok())
.map(|value| Value::String(value.trim().to_string())),
ConditionHeaders::Map(headers) => headers.get(&key).cloned().map(Value::String),
}
}
fn json_number(value: &Value) -> Option<f64> {
value.as_f64().filter(|_| !value.is_boolean())
}
fn parse_body_path(path: &str) -> Option<Vec<BodyPathSegment>> {
let raw = path.trim();
if raw.is_empty() {
return None;
}
let chars: Vec<char> = raw.chars().collect();
let mut parts = Vec::new();
let mut current = String::new();
let mut expect_key = true;
let mut index = 0usize;
while index < chars.len() {
let ch = chars[index];
if ch == '\\' && chars.get(index + 1).copied() == Some('.') {
current.push('.');
expect_key = false;
index += 2;
continue;
}
if ch == '.' {
if !current.is_empty() {
parts.push(BodyPathSegment::Key(std::mem::take(&mut current)));
} else if expect_key {
return None;
}
expect_key = true;
index += 1;
continue;
}
if ch == '[' {
if !current.is_empty() {
parts.push(BodyPathSegment::Key(std::mem::take(&mut current)));
}
let mut close_index = index + 1;
while close_index < chars.len() && chars[close_index] != ']' {
close_index += 1;
}
if close_index >= chars.len() {
return None;
}
let inner = chars[index + 1..close_index]
.iter()
.collect::<String>()
.trim()
.to_string();
if inner.is_empty() {
return None;
}
if inner == "*" {
parts.push(BodyPathSegment::WildcardSlice(None, None));
} else if let Some((start, end)) = parse_path_range(&inner) {
parts.push(BodyPathSegment::WildcardSlice(Some(start), Some(end)));
} else {
let Ok(index_value) = inner.parse::<isize>() else {
return None;
};
parts.push(BodyPathSegment::Index(index_value));
}
expect_key = false;
index = close_index + 1;
continue;
}
current.push(ch);
expect_key = false;
index += 1;
}
if !current.is_empty() {
parts.push(BodyPathSegment::Key(current));
} else if expect_key {
return None;
}
(!parts.is_empty()).then_some(parts)
}
fn parse_path_range(raw: &str) -> Option<(usize, usize)> {
let captures = range_regex().captures(raw)?;
let start = captures.get(1)?.as_str().parse::<usize>().ok()?;
let end = captures.get(2)?.as_str().parse::<usize>().ok()?;
Some((start, end))
}
fn range_regex() -> &'static Regex {
RANGE_RE.get_or_init(|| Regex::new(r"^(\d+)\s*-\s*(\d+)$").expect("valid range regex"))
}
fn has_wildcard(path: &[BodyPathSegment]) -> bool {
path.iter()
.any(|segment| matches!(segment, BodyPathSegment::WildcardSlice(..)))
}
fn wildcard_indices(
len: usize,
start: Option<usize>,
end: Option<usize>,
) -> Box<dyn Iterator<Item = usize>> {
if len == 0 {
return Box::new(std::iter::empty());
}
match (start, end) {
(None, None) => Box::new(0..len),
(Some(start), Some(end)) => {
let start = start.min(len);
let end = end.min(len.saturating_sub(1));
if start > end {
Box::new(std::iter::empty())
} else {
Box::new(start..(end + 1))
}
}
_ => Box::new(std::iter::empty()),
}
}
fn wildcard_matches_index(start: Option<usize>, end: Option<usize>, index: isize) -> bool {
match (start, end) {
(None, None) => true,
(Some(start), Some(end)) => usize::try_from(index)
.ok()
.is_some_and(|index| index >= start && index <= end),
_ => false,
}
}
fn path_matches(candidate: &[BodyPathSegment], target: &[BodyPathSegment]) -> bool {
candidate.len() == target.len()
&& candidate
.iter()
.zip(target)
.all(|(candidate, target)| match (candidate, target) {
(BodyPathSegment::Key(lhs), BodyPathSegment::Key(rhs)) => lhs == rhs,
(BodyPathSegment::Index(lhs), BodyPathSegment::Index(rhs)) => lhs == rhs,
(BodyPathSegment::WildcardSlice(start, end), BodyPathSegment::Index(index))
| (BodyPathSegment::Index(index), BodyPathSegment::WildcardSlice(start, end)) => {
wildcard_matches_index(*start, *end, *index)
}
(
BodyPathSegment::WildcardSlice(lhs_start, lhs_end),
BodyPathSegment::WildcardSlice(rhs_start, rhs_end),
) => lhs_start == rhs_start && lhs_end == rhs_end,
_ => false,
})
}
fn segments_to_path(path: &[BodyPathSegment]) -> String {
let mut parts = Vec::new();
for segment in path {
match segment {
BodyPathSegment::Index(index) => parts.push(format!("[{index}]")),
BodyPathSegment::WildcardSlice(None, None) => parts.push("[*]".to_string()),
BodyPathSegment::WildcardSlice(Some(start), Some(end)) => {
parts.push(format!("[{start}-{end}]"));
}
BodyPathSegment::WildcardSlice(..) => return String::new(),
BodyPathSegment::Key(key) => {
let escaped = key.replace('.', "\\.");
if parts
.last()
.is_some_and(|part: &String| !part.ends_with(']'))
{
parts.push(format!(".{escaped}"));
} else {
parts.push(escaped);
}
}
}
}
parts.join("")
}
fn contains_original_placeholder(value: &Value) -> bool {
match value {
Value::String(value) => value.contains(ORIGINAL_PLACEHOLDER),
Value::Array(items) => items.iter().any(contains_original_placeholder),
Value::Object(items) => items.values().any(contains_original_placeholder),
_ => false,
}
}
fn resolve_original_placeholder(template: &Value, original: Option<&Value>) -> Value {
match template {
Value::String(value) => {
if value == ORIGINAL_PLACEHOLDER {
return original.cloned().unwrap_or(Value::Null);
}
if value.contains(ORIGINAL_PLACEHOLDER) {
let replacement = original.map_or_else(|| "null".to_string(), placeholder_string);
return Value::String(value.replace(ORIGINAL_PLACEHOLDER, &replacement));
}
Value::String(value.clone())
}
Value::Array(items) => Value::Array(
items
.iter()
.map(|item| resolve_original_placeholder(item, original))
.collect(),
),
Value::Object(items) => Value::Object(
items
.iter()
.map(|(key, value)| (key.clone(), resolve_original_placeholder(value, original)))
.collect(),
),
_ => template.clone(),
}
}
fn placeholder_string(value: &Value) -> String {
match value {
Value::String(value) => value.clone(),
_ => value.to_string(),
}
}
fn resolve_index(len: usize, index: isize) -> Option<usize> {
if index >= 0 {
((index as usize) < len).then_some(index as usize)
} else {
let resolved = len as isize + index;
(resolved >= 0).then_some(resolved as usize)
}
}
fn normalize_insert_index(len: usize, index: isize) -> usize {
if index >= 0 {
(index as usize).min(len)
} else {
let resolved = len as isize + index;
resolved.max(0) as usize
}
}
fn expand_wildcard_paths(
value: &Value,
path: &[BodyPathSegment],
require_leaf: bool,
) -> Vec<Vec<BodyPathSegment>> {
let mut result = Vec::new();
let mut prefix = Vec::new();
expand_wildcard_paths_recursive(value, path, require_leaf, 0, &mut prefix, &mut result);
result
}
fn expand_wildcard_paths_recursive(
current: &Value,
path: &[BodyPathSegment],
require_leaf: bool,
offset: usize,
prefix: &mut Vec<BodyPathSegment>,
result: &mut Vec<Vec<BodyPathSegment>>,
) {
if offset == path.len() {
result.push(prefix.clone());
return;
}
let segment = &path[offset];
let is_last = offset == path.len() - 1;
match segment {
BodyPathSegment::WildcardSlice(start, end) => {
let Some(values) = current.as_array() else {
return;
};
for index in wildcard_indices(values.len(), *start, *end) {
prefix.push(BodyPathSegment::Index(index as isize));
expand_wildcard_paths_recursive(
&values[index],
path,
require_leaf,
offset + 1,
prefix,
result,
);
prefix.pop();
}
}
BodyPathSegment::Index(index) => {
let Some(values) = current.as_array() else {
return;
};
let Some(resolved) = resolve_index(values.len(), *index) else {
return;
};
prefix.push(BodyPathSegment::Index(*index));
expand_wildcard_paths_recursive(
&values[resolved],
path,
require_leaf,
offset + 1,
prefix,
result,
);
prefix.pop();
}
BodyPathSegment::Key(key) => {
let Some(object) = current.as_object() else {
return;
};
if let Some(next) = object.get(key) {
prefix.push(BodyPathSegment::Key(key.clone()));
expand_wildcard_paths_recursive(
next,
path,
require_leaf,
offset + 1,
prefix,
result,
);
prefix.pop();
} else if is_last && !require_leaf {
prefix.push(BodyPathSegment::Key(key.clone()));
result.push(prefix.clone());
prefix.pop();
}
}
}
}
struct WildcardTargetOptions<'a> {
condition: Option<&'a Value>,
item_condition: bool,
original_body: Option<&'a Value>,
request_headers: Option<ConditionHeaders<'a>>,
require_leaf: bool,
reverse: bool,
}
fn iter_wildcard_targets(
body: &Value,
path: &[BodyPathSegment],
options: WildcardTargetOptions<'_>,
) -> Vec<Vec<BodyPathSegment>> {
if !has_wildcard(path) {
return vec![path.to_vec()];
}
let mut expanded = expand_wildcard_paths(body, path, options.require_leaf);
if options.reverse {
expanded.reverse();
}
if !options.item_condition {
return expanded;
}
let Some(condition) = options.condition else {
return expanded;
};
expanded
.into_iter()
.filter(|concrete_path| {
let prefix = get_item_prefix_from_concrete(concrete_path, path);
let resolved = resolve_item_condition(condition, &prefix);
evaluate_local_condition(
body,
&resolved,
options.original_body,
options.request_headers,
)
})
.collect()
}
fn get_item_prefix_from_concrete(
concrete_path: &[BodyPathSegment],
wildcard_path: &[BodyPathSegment],
) -> String {
let last_wildcard_index = wildcard_path
.iter()
.enumerate()
.rev()
.find(|(_, segment)| matches!(segment, BodyPathSegment::WildcardSlice(..)))
.map(|(index, _)| index)
.unwrap_or(0);
segments_to_path(&concrete_path[..=last_wildcard_index])
}
fn condition_has_item_ref(condition: &Value) -> bool {
let Some(condition) = condition.as_object() else {
return false;
};
if let Some(children) = condition.get("all").and_then(Value::as_array) {
return children.iter().any(condition_has_item_ref);
}
if let Some(children) = condition.get("any").and_then(Value::as_array) {
return children.iter().any(condition_has_item_ref);
}
condition
.get("path")
.and_then(Value::as_str)
.map(str::trim)
.is_some_and(|path| path == ITEM_EXACT || path.starts_with(ITEM_PREFIX))
}
fn resolve_item_condition(condition: &Value, item_path_prefix: &str) -> Value {
let Some(condition) = condition.as_object() else {
return condition.clone();
};
if let Some(children) = condition.get("all").and_then(Value::as_array) {
return serde_json::json!({
"all": children
.iter()
.map(|child| resolve_item_condition(child, item_path_prefix))
.collect::<Vec<_>>()
});
}
if let Some(children) = condition.get("any").and_then(Value::as_array) {
return serde_json::json!({
"any": children
.iter()
.map(|child| resolve_item_condition(child, item_path_prefix))
.collect::<Vec<_>>()
});
}
let mut resolved = condition.clone();
if let Some(Value::String(path)) = resolved.get_mut("path") {
let trimmed = path.trim();
if trimmed == ITEM_EXACT {
*path = item_path_prefix.to_string();
} else if let Some(suffix) = trimmed.strip_prefix(ITEM_PREFIX) {
*path = format!("{item_path_prefix}.{suffix}");
}
}
Value::Object(resolved)
}
fn compile_regex(pattern: &str, flags: &str) -> Option<Regex> {
let mut builder = RegexBuilder::new(pattern);
for flag in flags.chars() {
match flag {
'i' => {
builder.case_insensitive(true);
}
'm' => {
builder.multi_line(true);
}
's' => {
builder.dot_matches_new_line(true);
}
_ => {}
}
}
builder.build().ok()
}
fn parse_insert_index(value: &Value) -> Option<isize> {
value
.as_i64()
.and_then(|value| isize::try_from(value).ok())
.or_else(|| value.as_u64().and_then(|value| isize::try_from(value).ok()))
}
fn parse_non_negative_count(value: &Value) -> Option<usize> {
value
.as_u64()
.and_then(|value| usize::try_from(value).ok())
.or_else(|| {
value.as_i64().and_then(|value| {
if value < 0 {
None
} else {
usize::try_from(value).ok()
}
})
})
}
fn get_nested_value(value: &Value, path: &[BodyPathSegment]) -> Option<Value> {
let mut current = value;
for segment in path {
match segment {
BodyPathSegment::Key(key) => {
current = current.as_object()?.get(key)?;
}
BodyPathSegment::Index(index) => {
let values = current.as_array()?;
let resolved = resolve_index(values.len(), *index)?;
current = values.get(resolved)?;
}
BodyPathSegment::WildcardSlice(..) => return None,
}
}
Some(current.clone())
}
fn get_nested_value_mut<'a>(
mut current: &'a mut Value,
path: &[BodyPathSegment],
) -> Option<&'a mut Value> {
for segment in path {
current = get_existing_child_mut(current, segment)?;
}
Some(current)
}
fn get_existing_child_mut<'a>(
current: &'a mut Value,
segment: &BodyPathSegment,
) -> Option<&'a mut Value> {
match segment {
BodyPathSegment::Key(key) => current.as_object_mut()?.get_mut(key),
BodyPathSegment::Index(index) => {
let values = current.as_array_mut()?;
let resolved = resolve_index(values.len(), *index)?;
values.get_mut(resolved)
}
BodyPathSegment::WildcardSlice(..) => None,
}
}
fn set_nested_value(current: &mut Value, path: &[BodyPathSegment], value: Value) -> bool {
let Some((last, parents)) = path.split_last() else {
return false;
};
let mut current = current;
for (offset, segment) in parents.iter().enumerate() {
let next = &path[offset + 1];
match segment {
BodyPathSegment::Key(key) => {
let Some(object) = current.as_object_mut() else {
return false;
};
match next {
BodyPathSegment::Key(_) => {
let child = object
.entry(key.clone())
.or_insert_with(|| Value::Object(Map::new()));
if !child.is_object() {
*child = Value::Object(Map::new());
}
current = child;
}
BodyPathSegment::Index(_) => {
let Some(child) = object.get_mut(key) else {
return false;
};
if !child.is_array() {
return false;
}
current = child;
}
BodyPathSegment::WildcardSlice(..) => return false,
}
}
BodyPathSegment::Index(index) => {
let Some(values) = current.as_array_mut() else {
return false;
};
let Some(resolved) = resolve_index(values.len(), *index) else {
return false;
};
current = &mut values[resolved];
}
BodyPathSegment::WildcardSlice(..) => return false,
}
}
match last {
BodyPathSegment::Key(key) => {
let Some(object) = current.as_object_mut() else {
return false;
};
object.insert(key.clone(), value);
true
}
BodyPathSegment::Index(index) => {
let Some(values) = current.as_array_mut() else {
return false;
};
let Some(resolved) = resolve_index(values.len(), *index) else {
return false;
};
values[resolved] = value;
true
}
BodyPathSegment::WildcardSlice(..) => false,
}
}
fn delete_nested_value(current: &mut Value, path: &[BodyPathSegment]) -> bool {
let Some((last, parents)) = path.split_last() else {
return false;
};
let mut current = current;
for segment in parents {
let Some(child) = get_existing_child_mut(current, segment) else {
return false;
};
current = child;
}
match last {
BodyPathSegment::Key(key) => current
.as_object_mut()
.and_then(|object| object.remove(key))
.is_some(),
BodyPathSegment::Index(index) => {
let Some(values) = current.as_array_mut() else {
return false;
};
let Some(resolved) = resolve_index(values.len(), *index) else {
return false;
};
values.remove(resolved);
true
}
BodyPathSegment::WildcardSlice(..) => false,
}
}
fn rename_nested_value(
current: &mut Value,
from: &[BodyPathSegment],
to: &[BodyPathSegment],
) -> bool {
if from == to {
return get_nested_value(current, from).is_some();
}
let Some(value) = get_nested_value(current, from) else {
return false;
};
if !set_nested_value(current, to, value) {
return false;
}
delete_nested_value(current, from)
}
#[cfg(test)]
mod tests {
use super::{
apply_local_body_rules, apply_local_body_rules_with_request_headers,
apply_local_header_rules, apply_local_header_rules_with_request_headers,
body_rules_are_locally_supported, body_rules_handle_path, body_rules_have_enabled_rules,
header_rules_are_locally_supported, header_rules_have_enabled_rules,
};
#[test]
fn header_rules_allow_simple_set_drop_and_rename() {
let rules = serde_json::json!([
{"action":"set","key":"x-added","value":"1"},
{"action":"drop","key":"x-drop"},
{"action":"rename","from":"x-old","to":"x-new"}
]);
assert!(header_rules_are_locally_supported(Some(&rules)));
let mut headers = std::collections::BTreeMap::from([
("x-drop".to_string(), "drop-me".to_string()),
("x-old".to_string(), "old-value".to_string()),
("authorization".to_string(), "Bearer keep".to_string()),
]);
assert!(apply_local_header_rules(
&mut headers,
Some(&rules),
&["authorization", "content-type"],
&serde_json::json!({}),
None,
));
assert_eq!(headers.get("x-added").map(String::as_str), Some("1"));
assert!(!headers.contains_key("x-drop"));
assert_eq!(headers.get("x-new").map(String::as_str), Some("old-value"));
assert_eq!(
headers.get("authorization").map(String::as_str),
Some("Bearer keep")
);
}
#[test]
fn header_rules_allow_simple_conditions() {
let rules = serde_json::json!([
{"action":"set","key":"x-added","value":"1","condition":{"path":"metadata.mode","op":"eq","value":"safe"}},
{"action":"set","key":"x-from-original","value":"1","condition":{"path":"metadata.client","op":"exists","source":"original"}}
]);
assert!(header_rules_are_locally_supported(Some(&rules)));
let mut headers = std::collections::BTreeMap::new();
assert!(apply_local_header_rules(
&mut headers,
Some(&rules),
&[],
&serde_json::json!({"metadata":{"mode":"safe"}}),
Some(&serde_json::json!({"metadata":{"mode":"safe","client":"desktop"}})),
));
assert_eq!(headers.get("x-added").map(String::as_str), Some("1"));
assert_eq!(
headers.get("x-from-original").map(String::as_str),
Some("1")
);
}
#[test]
fn header_rules_can_read_original_request_header_conditions() {
let rules = serde_json::json!([
{"action":"set","key":"x-applied","value":"yes","condition":{"source":"request_headers","path":"X-Mode","op":"eq","value":"debug"}},
{"action":"set","key":"x-skipped","value":"yes","condition":{"source":"request_headers","path":"x-missing","op":"exists"}}
]);
let mut request_headers = http::HeaderMap::new();
request_headers.insert("x-mode", "debug".parse().unwrap());
let mut headers = std::collections::BTreeMap::from([(
"x-mode".to_string(),
"provider-value".to_string(),
)]);
assert!(apply_local_header_rules_with_request_headers(
&mut headers,
Some(&rules),
&[],
&serde_json::json!({}),
None,
Some(&request_headers),
));
assert_eq!(headers.get("x-applied").map(String::as_str), Some("yes"));
assert!(!headers.contains_key("x-skipped"));
}
#[test]
fn body_rules_support_all_runtime_actions() {
let rules = serde_json::json!([
{"action":"set","path":"model","value":"wrapped-{{$original}}"},
{"action":"set","path":"metadata.snapshot","value":"{{$original}}","condition":{"path":"metadata.snapshot","op":"exists"}},
{"action":"rename","from":"messages[0].content","to":"messages[0].text"},
{"action":"append","path":"messages","value":{"role":"assistant","content":"done"}},
{"action":"insert","path":"messages","index":-1,"value":{"role":"system","content":"before-last"}},
{"action":"regex_replace","path":"tools[*].name","pattern":"tool","replacement":"utility","flags":"i","count":1},
{"action":"drop","path":"tools[1-2].deprecated"}
]);
assert!(body_rules_are_locally_supported(Some(&rules)));
let mut body = serde_json::json!({
"model": "gpt-5",
"metadata": {
"snapshot": {
"keep": true
}
},
"messages": [
{"content":"hello"},
{"content":"world"}
],
"tools": [
{"name":"WriterTool","kind":"snake_case_name","deprecated":true},
{"name":"ReadTool","kind":"Pascal Case","deprecated":true},
{"name":"OtherTool","kind":"kebab-case-value","deprecated":true}
]
});
assert!(apply_local_body_rules(&mut body, Some(&rules), None));
assert_eq!(body["model"], "wrapped-gpt-5");
assert_eq!(
body["metadata"]["snapshot"],
serde_json::json!({"keep": true})
);
assert_eq!(body["messages"][0]["text"], "hello");
assert!(body["messages"][0].get("content").is_none());
assert_eq!(body["messages"][1]["content"], "world");
assert_eq!(
body["messages"][2],
serde_json::json!({"role":"system","content":"before-last"})
);
assert_eq!(
body["messages"][3],
serde_json::json!({"role":"assistant","content":"done"})
);
assert_eq!(body["tools"][0]["name"], "Writerutility");
assert_eq!(body["tools"][1]["name"], "Readutility");
assert_eq!(body["tools"][2]["name"], "Otherutility");
assert_eq!(body["tools"][0]["deprecated"], true);
assert!(body["tools"][1].get("deprecated").is_none());
assert!(body["tools"][2].get("deprecated").is_none());
}
#[test]
fn body_rules_conditions_cover_all_supported_operators() {
let rules = serde_json::json!([
{"action":"set","path":"results.eq","value":true,"condition":{"path":"text","op":"eq","value":"alpha-beta"}},
{"action":"set","path":"results.neq","value":true,"condition":{"path":"text","op":"neq","value":"other"}},
{"action":"set","path":"results.gt","value":true,"condition":{"path":"num","op":"gt","value":9}},
{"action":"set","path":"results.lt","value":true,"condition":{"path":"num","op":"lt","value":11}},
{"action":"set","path":"results.gte","value":true,"condition":{"path":"num","op":"gte","value":10}},
{"action":"set","path":"results.lte","value":true,"condition":{"path":"num","op":"lte","value":10}},
{"action":"set","path":"results.starts_with","value":true,"condition":{"path":"text","op":"starts_with","value":"alpha"}},
{"action":"set","path":"results.ends_with","value":true,"condition":{"path":"text","op":"ends_with","value":"beta"}},
{"action":"set","path":"results.contains_string","value":true,"condition":{"path":"text","op":"contains","value":"ha-be"}},
{"action":"set","path":"results.contains_array","value":true,"condition":{"path":"tags","op":"contains","value":"green"}},
{"action":"set","path":"results.matches","value":true,"condition":{"path":"text","op":"matches","value":"alpha.*beta"}},
{"action":"set","path":"results.exists","value":true,"condition":{"path":"profile.name","op":"exists"}},
{"action":"set","path":"results.not_exists","value":true,"condition":{"path":"profile.age","op":"not_exists"}},
{"action":"set","path":"results.in","value":true,"condition":{"path":"choice","op":"in","value":["a","b","c"]}},
{"action":"set","path":"results.type_is","value":true,"condition":{"path":"flag","op":"type_is","value":"boolean"}},
{"action":"set","path":"results.original","value":true,"condition":{"path":"legacy.present","op":"exists","source":"original"}},
{"action":"set","path":"results.all","value":true,"condition":{"all":[{"path":"num","op":"gt","value":5},{"path":"text","op":"contains","value":"beta"}]}},
{"action":"set","path":"results.any","value":true,"condition":{"any":[{"path":"missing","op":"exists"},{"path":"maybe_null","op":"type_is","value":"null"}]}}
]);
assert!(body_rules_are_locally_supported(Some(&rules)));
let original = serde_json::json!({
"num": 10,
"text": "alpha-beta",
"tags": ["red", "green"],
"choice": "b",
"flag": true,
"maybe_null": null,
"profile": {
"name": "Ada"
},
"legacy": {
"present": 1
}
});
let mut body = serde_json::json!({
"num": 10,
"text": "alpha-beta",
"tags": ["red", "green"],
"choice": "b",
"flag": true,
"maybe_null": null,
"profile": {
"name": "Ada"
}
});
assert!(apply_local_body_rules(
&mut body,
Some(&rules),
Some(&original)
));
let expected = serde_json::json!({
"eq": true,
"neq": true,
"gt": true,
"lt": true,
"gte": true,
"lte": true,
"starts_with": true,
"ends_with": true,
"contains_string": true,
"contains_array": true,
"matches": true,
"exists": true,
"not_exists": true,
"in": true,
"type_is": true,
"original": true,
"all": true,
"any": true
});
assert_eq!(body["results"], expected);
}
#[test]
fn body_conditions_default_to_current_request_body() {
let rules = serde_json::json!([
{"action":"set","path":"model","value":"provider-model"},
{"action":"set","path":"metadata.original_hit","value":true,"condition":{"path":"model","op":"eq","value":"client-model"}},
{"action":"set","path":"metadata.current_hit","value":true,"condition":{"path":"model","op":"eq","value":"provider-model"}}
]);
let original = serde_json::json!({
"model": "client-model"
});
let mut body = original.clone();
assert!(apply_local_body_rules(
&mut body,
Some(&rules),
Some(&original)
));
assert_eq!(body["model"], "provider-model");
assert!(body["metadata"].get("original_hit").is_none());
assert_eq!(body["metadata"]["current_hit"], true);
}
#[test]
fn body_conditions_default_to_current_request_body_for_wildcard_drop() {
let rules = serde_json::json!([
{
"action": "drop",
"path": "tools[*]",
"condition": {"path": "$item.name", "op": "eq", "value": "Agent"}
}
]);
let original = serde_json::json!({
"model": "client-model"
});
let mut body = serde_json::json!({
"tools": [
{"name": "Agent"},
{"name": "Bash"}
]
});
assert!(apply_local_body_rules(
&mut body,
Some(&rules),
Some(&original)
));
assert_eq!(body["tools"], serde_json::json!([{"name": "Bash"}]));
}
#[test]
fn body_rules_can_read_original_request_header_conditions() {
let rules = serde_json::json!([
{"action":"set","path":"metadata.from_header","value":true,"condition":{"source":"request_headers","path":"X-Mode","op":"eq","value":"debug"}},
{"action":"set","path":"metadata.contains","value":true,"condition":{"source":"headers","path":"x-feature","op":"contains","value":"beta"}},
{"action":"set","path":"metadata.skipped","value":true,"condition":{"source":"request_headers","path":"x-mode","op":"eq","value":"prod"}}
]);
let mut request_headers = http::HeaderMap::new();
request_headers.insert("x-mode", "debug".parse().unwrap());
request_headers.insert("x-feature", "alpha,beta".parse().unwrap());
let mut body = serde_json::json!({});
assert!(apply_local_body_rules_with_request_headers(
&mut body,
Some(&rules),
None,
Some(&request_headers),
));
assert_eq!(body["metadata"]["from_header"], true);
assert_eq!(body["metadata"]["contains"], true);
assert!(body["metadata"].get("skipped").is_none());
}
#[test]
fn body_rules_tolerate_invalid_regex_flags_and_negative_count() {
let invalid_flags = serde_json::json!([
{"action":"regex_replace","path":"text","pattern":"foo","replacement":"bar","flags":"ix"}
]);
let invalid_count = serde_json::json!([
{"action":"regex_replace","path":"text","pattern":"foo","replacement":"bar","count":-1}
]);
let mut flags_body = serde_json::json!({"text":"foo"});
let mut count_body = serde_json::json!({"text":"foo foo"});
assert!(body_rules_are_locally_supported(Some(&invalid_flags)));
assert!(body_rules_are_locally_supported(Some(&invalid_count)));
assert!(apply_local_body_rules(
&mut flags_body,
Some(&invalid_flags),
None
));
assert!(apply_local_body_rules(
&mut count_body,
Some(&invalid_count),
None
));
assert_eq!(flags_body["text"], "bar");
assert_eq!(count_body["text"], "bar bar");
}
#[test]
fn body_rules_skip_invalid_entries_without_rejecting_whole_body() {
let rules = serde_json::json!([
{"action":"set","path":".bad","value":1},
{"action":"drop","path":"missing."},
{"action":"regex_replace","path":"text","pattern":"(","replacement":"x"},
{"op":"remove","path":"/legacy"},
{"action":"set","path":"ok","value":true}
]);
let mut body = serde_json::json!({
"text": "keep",
"legacy": true
});
assert!(apply_local_body_rules(&mut body, Some(&rules), None));
assert_eq!(body["text"], "keep");
assert_eq!(body["legacy"], true);
assert_eq!(body["ok"], true);
}
#[test]
fn body_rules_skip_disabled_entries() {
let rules = serde_json::json!([
{"enabled":false,"action":"set","path":"metadata.disabled","value":true},
{"enabled":false,"action":"drop","path":"keep"},
{"action":"set","path":"metadata.enabled","value":true}
]);
let mut body = serde_json::json!({
"keep": true,
"metadata": {}
});
assert!(body_rules_have_enabled_rules(Some(&rules)));
assert!(apply_local_body_rules(&mut body, Some(&rules), None));
assert_eq!(body["keep"], true);
assert!(body["metadata"].get("disabled").is_none());
assert_eq!(body["metadata"]["enabled"], true);
assert!(!body_rules_handle_path(Some(&rules), "metadata.disabled"));
assert!(body_rules_handle_path(Some(&rules), "metadata.enabled"));
let disabled_only = serde_json::json!([
{"enabled":false,"action":"set","path":"metadata.disabled","value":true}
]);
assert!(!body_rules_have_enabled_rules(Some(&disabled_only)));
}
#[test]
fn header_rules_skip_invalid_entries_without_rejecting_whole_headers() {
let rules = serde_json::json!([
{"action":"set","key":"","value":"bad"},
{"action":"set","key":"x-json","value":{"nested":true}},
{"action":"drop","key":null},
{"action":"rename","from":"x-missing","to":""},
{"op":"remove","key":"x-legacy"},
{"action":"set","key":"x-ok","value":"yes"}
]);
let mut headers = std::collections::BTreeMap::from([(
"authorization".to_string(),
"Bearer keep".to_string(),
)]);
assert!(header_rules_are_locally_supported(Some(&rules)));
assert!(apply_local_header_rules(
&mut headers,
Some(&rules),
&["authorization"],
&serde_json::json!({}),
None,
));
assert_eq!(
headers.get("authorization").map(String::as_str),
Some("Bearer keep")
);
assert_eq!(
headers.get("x-json").map(String::as_str),
Some("{\"nested\":true}")
);
assert_eq!(headers.get("x-ok").map(String::as_str), Some("yes"));
assert!(!headers.contains_key("x-legacy"));
}
#[test]
fn header_rules_skip_disabled_entries() {
let rules = serde_json::json!([
{"enabled":false,"action":"set","key":"x-disabled","value":"bad"},
{"enabled":false,"action":"drop","key":"x-keep"},
{"action":"set","key":"x-enabled","value":"ok"}
]);
assert!(header_rules_have_enabled_rules(Some(&rules)));
let mut headers =
std::collections::BTreeMap::from([("x-keep".to_string(), "yes".to_string())]);
assert!(apply_local_header_rules(
&mut headers,
Some(&rules),
&[],
&serde_json::json!({}),
None,
));
assert_eq!(headers.get("x-keep").map(String::as_str), Some("yes"));
assert!(!headers.contains_key("x-disabled"));
assert_eq!(headers.get("x-enabled").map(String::as_str), Some("ok"));
let disabled_only = serde_json::json!([
{"enabled":false,"action":"set","key":"x-disabled","value":"bad"}
]);
assert!(!header_rules_have_enabled_rules(Some(&disabled_only)));
}
#[test]
fn body_rules_handle_exact_and_wildcard_paths() {
let rules = serde_json::json!([
{"action":"append","path":"messages","value":{}},
{"action":"regex_replace","path":"tools[*].name","pattern":"foo","replacement":"bar"},
{"action":"rename","from":"metadata.old","to":"metadata.new"}
]);
assert!(body_rules_handle_path(Some(&rules), "messages"));
assert!(body_rules_handle_path(Some(&rules), "tools[1].name"));
assert!(body_rules_handle_path(Some(&rules), "metadata.old"));
assert!(body_rules_handle_path(Some(&rules), "metadata.new"));
assert!(!body_rules_handle_path(Some(&rules), "tools[2].kind"));
assert!(!body_rules_handle_path(Some(&rules), "tools[3].kind"));
assert!(!body_rules_handle_path(Some(&rules), "instructions"));
}
}