// preprocess.mbt — Matcher and effect text preprocessing.
//
// Casbin preprocesses `[policy_effect]` and `[matchers]` values when a
// model is loaded: comments are stripped, `r.sub` / `p.obj` style
// accessors become the flat tokens `r_sub` / `p_obj`, and — for matchers
// only — string literals are escaped for govaluate and `[` / `]` become
// `(` / `)` when the expression mentions `in`.
//
// This module implements the two steps our lexer needs:
//
// - `remove_comments` mirrors Casbin: the first `#` starts a comment and
// everything from it on is dropped (the kept prefix is trimmed).
// - `escape_assertion` mirrors Casbin's `EscapeAssertion`: the dot right
// after `r`, `p`, `r`, or `p` becomes `_` when the token
// starts the text or is preceded by one of `( ) whitespace | & , = ! > <
// + - * /`. Field access after that first dot is preserved, so
// `r.sub.Age` becomes `r_sub.Age`.
//
// Two Casbin steps are intentionally not replicated: `EscapeStringLiterals`
// is unnecessary because this lexer handles single- and double-quoted
// strings natively, and the `in` bracket rewrite is unnecessary because the
// parser accepts both `('a', 'b')` and `['a', 'b']` lists.
///|
/// Strips a `#` comment; the kept prefix is trimmed. Mirrors Casbin's
/// `RemoveComments` (including returning the text unchanged when there is
/// no `#`).
pub fn remove_comments(text : String) -> String {
match text.find("#") {
None => text
Some(index) => text[:index].trim().to_owned()
}
}
///|
/// Rewrites the first dot of `r.attr` style accessors to `_`, mirroring
/// Casbin's `EscapeAssertion`.
pub fn escape_assertion(text : String) -> String {
let chars : Array[Char] = text.iter().collect()
let length = chars.length()
let result : Array[Char] = []
let mut index = 0
while index < length {
let ch = chars[index]
if (ch == 'r' || ch == 'p') &&
(index == 0 || is_escape_prefix(chars[index - 1])) {
let mut digits_end = index + 1
while digits_end < length && chars[digits_end].is_ascii_digit() {
digits_end += 1
}
if digits_end < length && chars[digits_end] == '.' {
for i in index.. String {
remove_comments(escape_assertion(text))
}
///|
/// Characters that may precede an `r.` / `p.` accessor: Casbin's regex
/// uses `[()\s|&,=!><+\-*/]` or the start of the text.
fn is_escape_prefix(ch : Char) -> Bool {
ch == '(' ||
ch == ')' ||
ch.is_whitespace() ||
ch == '|' ||
ch == '&' ||
ch == ',' ||
ch == '=' ||
ch == '!' ||
ch == '>' ||
ch == '<' ||
ch == '+' ||
ch == '-' ||
ch == '*' ||
ch == '/'
}