// adapter_csv.mbt — Policy text parsing (Casbin's CSV policy format).
//
// Each non-empty, non-comment line is one policy rule:
//
//     p, alice, data1, read
//     g, alice, admin
//
// The first field is the policy type (`p`, `p2`, `g`, `g2`, ...) and the
// remaining fields are the rule values. Fields follow RFC 4180 quoting: a
// field starting with `"` ends at the matching quote, and `""` is a
// literal quote. Lines starting with `#` are comments.
//
// Casbin parses lines with Go's `encoding/csv` and `TrimLeadingSpace`; this
// implementation trims both ends of unquoted fields, a deliberate
// simplification (trailing spaces in policy values are almost always
// accidental). Quoted field contents are preserved exactly.

///|
/// One parsed policy line: the policy type plus the rule values.
pub(all) struct PolicyLine {
  key : String
  values : Array[String]
} derive(Eq, Debug)

///|
/// The policy type, for example `p` or `g`.
pub fn PolicyLine::key(self : PolicyLine) -> String {
  self.key
}

///|
/// The rule values, without the policy type.
pub fn PolicyLine::values(self : PolicyLine) -> Array[String] {
  self.values
}

///|
/// Parses policy text into lines. Empty lines and `#` comments are
/// skipped; a line whose first field is empty raises `PolicySyntax`.
pub fn parse_policy_text(
  text : String,
) -> Result[Array[PolicyLine], CasbinError] {
  Ok(parse_policy_text_raise(text)) catch {
    error => Err(error)
  }
}

///|
fn parse_policy_text_raise(
  text : String,
) -> Array[PolicyLine] raise CasbinError {
  let lines : Array[PolicyLine] = []
  for raw_line in text.split("\n") {
    let line = raw_line.trim().to_owned()
    if line.is_empty() || line.has_prefix("#") {
      continue
    }
    let fields = parse_csv_line(line)
    if fields.is_empty() || fields[0].is_empty() {
      raise casbin_error(
        PolicySyntax,
        "invalid policy rule: missing policy type",
      )
    }
    let values : Array[String] = []
    for i in 1.. Array[String] raise CasbinError {
  let chars : Array[Char] = line.iter().collect()
  let length = chars.length()
  let fields : Array[String] = []
  let mut index = 0
  while true {
    while index < length && (chars[index] == ' ' || chars[index] == '\t') {
      index += 1
    }
    if index < length && chars[index] == '"' {
      let value : Array[Char] = []
      let mut closed = false
      index += 1
      while index < length {
        let ch = chars[index]
        if ch == '"' {
          if index + 1 < length && chars[index + 1] == '"' {
            value.push('"')
            index += 2
            continue
          }
          index += 1
          closed = true
          break
        }
        value.push(ch)
        index += 1
      }
      if !closed {
        raise casbin_error(PolicySyntax, "unterminated quoted field")
      }
      while index < length && (chars[index] == ' ' || chars[index] == '\t') {
        index += 1
      }
      if index < length && chars[index] != ',' {
        raise casbin_error(
          PolicySyntax,
          "unexpected character after a quoted field",
        )
      }
      fields.push(StringView::from_iter(value.iter()).to_owned())
    } else {
      let value : Array[Char] = []
      while index < length && chars[index] != ',' {
        value.push(chars[index])
        index += 1
      }
      fields.push(
        StringView::from_iter(value.iter()).to_owned().trim().to_owned(),
      )
    }
    if index < length {
      index += 1
      continue
    }
    break
  }
  fields
}