#[inline]
pub fn apply_falsely_claim(source: &str, law: &str, op: &str) -> String {
replace_in_op_scope(source, op, law, &format!("FalselyClaimed({})", law))
}
#[inline]
pub fn apply_identity_corrupt(source: &str, op: &str, wrong: u32) -> String {
replace_in_op_scope(source, op, "identity: 0", &format!("identity: {}", wrong))
}
#[inline]
pub fn apply_absorbing_corrupt(source: &str, op: &str, wrong: u32) -> String {
replace_in_op_scope(source, op, "absorbing: 0", &format!("absorbing: {}", wrong))
}
fn replace_in_op_scope(source: &str, op: &str, pattern: &str, replacement: &str) -> String {
let Some(scope) = op_scope(source, op) else {
return source.to_string();
};
let inner = &source[scope.clone()];
let replaced = crate::adversarial::mutations::catalog::lexical::replace_code(
inner,
pattern,
replacement,
1,
);
if replaced == inner {
return source.to_string();
}
format!(
"{}{}{}",
&source[..scope.start],
replaced,
&source[scope.end..]
)
}
fn op_scope(source: &str, op: &str) -> Option<std::ops::Range<usize>> {
let id_patterns = [
format!("id: \"{op}\""),
format!("op: \"{op}\""),
format!("op_id: \"{op}\""),
];
for pattern in id_patterns {
if let Some(id_pos) =
crate::adversarial::mutations::catalog::lexical::find_code(source, &pattern)
{
let open = source[..id_pos].rfind('{').unwrap_or(0);
let close = matching_close_brace(source, open)?;
return Some(open..close);
}
}
None
}
fn matching_close_brace(source: &str, open: usize) -> Option<usize> {
let mut depth = 0usize;
let mut i = open;
while i < source.len() {
if !crate::adversarial::mutations::catalog::lexical::is_code_index(source, i) {
i += source[i..].chars().next()?.len_utf8();
continue;
}
let ch = source[i..].chars().next()?;
if ch == '{' {
depth += 1;
} else if ch == '}' {
depth = depth.checked_sub(1)?;
if depth == 0 {
return Some(i + ch.len_utf8());
}
}
i += ch.len_utf8();
}
None
}