///|
pub(all) struct MaildirName {
  unique : String
  flags : String
  has_info : Bool
} derive(Debug, Eq)

///|
pub fn parse_maildir_name(name : String) -> MaildirName raise MailboxError {
  if name == "" ||
    name == "." ||
    name == ".." ||
    name.contains("/") ||
    name.contains("\\") ||
    name.contains("\u0000") ||
    name.contains("\r") ||
    name.contains("\n") {
    raise Invalid("expected safe basename")
  }
  let pos = name.find(":2,")
  let unique = match pos {
    Some(i) => name[:i].to_owned()
    None => name
  }
  let flags = match pos {
    Some(i) => name[i + 3:].to_owned()
    None => ""
  }
  if unique == "" || unique.contains(":") {
    raise Invalid("invalid Maildir unique name or info version")
  }
  let seen : Array[Char] = []
  for c in flags.iter() {
    if !(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') {
      raise Invalid("invalid Maildir flag")
    }
    if seen.contains(c) {
      raise Invalid("duplicate Maildir flag")
    }
    seen.push(c)
  }
  seen.sort()
  let mut sorted = ""
  for c in seen {
    sorted += c.to_string()
  }
  { unique, flags: sorted, has_info: pos is Some(_), }
}

///|
pub fn MaildirName::encode(self : MaildirName) -> String raise MailboxError {
  if !self.has_info && self.flags != "" {
    raise Invalid("flags require info suffix")
  }
  let candidate = self.unique +
    (if self.has_info { ":2," + self.flags } else { "" })
  let parsed = parse_maildir_name(candidate)
  if parsed.unique != self.unique {
    raise Invalid("unique name contains an info suffix")
  }
  parsed.unique + (if parsed.has_info { ":2," + parsed.flags } else { "" })
}