///|
/// Block visibility conditions, following boofuzz/blocks/block.py
/// `_do_dependencies_allow_render` at 518c13904fc32e7f2cc88c9dec934e509062953e.
/// The ordering variants keep upstream's operand order: upstream evaluates
/// `dep_value OP dependent_value` (the configured threshold on the left),
/// so `Greater` renders when the current field value is LESS than the
/// configured bytes, not greater. Preserved for definition parity.
pub(all) enum Condition {
Equal(String, Bytes)
NotEqual(String, Bytes)
OneOf(String, Array[Bytes])
NotOneOf(String, Array[Bytes])
Greater(String, Bytes)
GreaterEqual(String, Bytes)
Less(String, Bytes)
LessEqual(String, Bytes)
}
///|
fn Condition::path(self : Condition) -> String {
match self {
Equal(path, _)
| NotEqual(path, _)
| OneOf(path, _)
| NotOneOf(path, _)
| Greater(path, _)
| GreaterEqual(path, _)
| Less(path, _)
| LessEqual(path, _) => path
}
}
///|
/// Python bytes comparison: lexicographic over unsigned byte values.
fn bytes_lexicographic(left : Bytes, right : Bytes) -> Int {
let shared = if left.length() < right.length() {
left.length()
} else {
right.length()
}
for i in 0.. right.length() {
1
} else {
0
}
}
///|
pub fn Block::when(self : Block, condition : Condition) -> Block {
let saved = match condition {
OneOf(path, values) => OneOf(path, values.copy())
NotOneOf(path, values) => NotOneOf(path, values.copy())
other => other
}
{ ..self, condition: Some(saved), }
}
///|
fn CompiledRequest::is_visible(
self : CompiledRequest,
index : Int,
replacements : Replacements,
vars : Map[String, Bytes]?,
) -> Bool raise ModelError {
for current = Some(index)
current is Some(i)
current = self.entries[i].parent {
if self.entries[i].condition is Some(condition) {
let target = self.resolve(condition.path())
let value = match replacements.lookup(target) {
Some(value) => value
None =>
match self.entries[target].kind {
Atom(field) => field.value
Dynamic(field, variable) =>
match vars {
Some(map) =>
match map.get(variable) {
Some(bytes) => bytes
None =>
raise Invalid("missing session variable: " + variable)
}
None => field.value
}
_ => raise Invalid("condition target must be a field")
}
}
let matches = match condition {
Equal(_, expected) => value == expected
NotEqual(_, expected) => value != expected
OneOf(_, expected) => expected.contains(value)
NotOneOf(_, expected) => !expected.contains(value)
Greater(_, expected) => bytes_lexicographic(expected, value) > 0
GreaterEqual(_, expected) => bytes_lexicographic(expected, value) >= 0
Less(_, expected) => bytes_lexicographic(expected, value) < 0
LessEqual(_, expected) => bytes_lexicographic(expected, value) <= 0
}
if !matches {
return false
}
}
}
true
}