1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::collections::HashMap;
use crate::{flag::Flag, module_flag::ModuleFlags};
/// `FlagParser` acts as the central engine for interpreting and evaluating bit-based flags.
///
/// It maintains a registry of "Modules," where each module is identified by a unique name
/// and contains a bitmask representing various toggled states.
pub struct FlagParser {
/// A map where keys are module names (e.g., "Core", "Network") and values
/// are the actual bitmask wrappers (`ModuleFlags`).
pub module_flags: HashMap<String, ModuleFlags>,
}
impl FlagParser {
/// Initializes a fresh parser with an empty registry.
pub fn new() -> Self {
Self {
module_flags: HashMap::new(),
}
}
/// Parses a raw configuration string and populates the module registry.
///
/// # Expected Format
/// `module_name:binary_bits` (e.g., "Network:1011 Core:001")
///
/// # Logic
/// 1. Splits the input string into individual tokens by whitespace.
/// 2. For each token, splits at the `:` to separate the name from the bitmask.
/// 3. Reverses the bits (right-to-left) so that index 0 corresponds to the least significant bit.
pub fn parse_flag_string(&mut self, flag_str: &str) {
for token in flag_str.split_whitespace() {
if let Some((name, bits)) = token.split_once(':') {
let mut module = ModuleFlags::new();
// We iterate through bits in reverse because binary strings read
// most-significant to least-significant (e.g., '10' means index 1 is set).
for (i, c) in bits.chars().rev().enumerate() {
if c == '1' {
module.set_flag(i as u64);
}
}
self.module_flags.insert(name.to_string(), module);
}
}
}
/// Entry point for the boolean expression engine.
///
/// Evaluates complex strings like `!Network.Flag1 && Core.Flag0`.
pub fn check_complex_pattern(&self, pattern: &str) -> bool {
let tokens = Self::tokenize(pattern);
Self::eval_tokens(&tokens, &self.module_flags)
}
/// Pre-processor for the logic engine.
///
/// Injects spaces around logical operators and negations to ensure the
/// split-by-whitespace method can capture them accurately.
fn tokenize(input: &str) -> Vec<String> {
input
.replace("&&", " && ")
.replace("||", " || ")
.replace("!", " !") // Allows !Module.Flag to be handled as a prefix or separate token
.split_whitespace()
.map(String::from)
.collect()
}
/// Core evaluation loop for tokenized boolean logic.
///
/// This is a basic "Linear Evaluator" (it processes left-to-right).
/// Note: It does not currently support operator precedence (Parentheses).
fn eval_tokens(tokens: &[String], flags: &HashMap<String, ModuleFlags>) -> bool {
let mut result = None;
let mut current_op = None;
for token in tokens {
match token.as_str() {
// Set the pending operation for the next value encountered
"&&" => current_op = Some("&&"),
"||" => current_op = Some("||"),
_ => {
// Check for '!' negation prefix
let negated = token.starts_with('!');
let flag_expr = token.trim_start_matches('!');
// Parse the "Module.FlagX" format
let value = match flag_expr.split_once('.') {
Some((module, index_str)) => {
// Extract numeric index by stripping the "Flag" prefix
if let Ok(index) = index_str.trim_start_matches("Flag").parse::<u64>() {
flags.get(module)
.map(|m| m.check_flag(index))
.unwrap_or(false) // Module not found = False
} else {
false // Invalid index format = False
}
}
None => false, // No '.' separator = False
};
// Apply negation if '!' was present
let value = if negated { !value } else { value };
// Combine the parsed value with the existing running total
result = Some(match (result, current_op) {
(Some(lhs), Some("&&")) => lhs && value,
(Some(lhs), Some("||")) => lhs || value,
(None, _) => value, // First token in the expression
(Some(_), _) => value, // Safety fallback for malformed ops
});
}
}
}
result.unwrap_or(false)
}
}