///|
pub(all) enum FeedFormat {
  JsonFeed
  Rss
  Atom
} derive(Eq, @debug.Debug)

///|
pub fn FeedFormat::equal(self : FeedFormat, other : FeedFormat) -> Bool {
  Eq::equal(self, other)
}

///|
pub fn FeedFormat::not_equal(self : FeedFormat, other : FeedFormat) -> Bool {
  !Eq::equal(self, other)
}

///|
pub fn FeedFormat::to_repr(self : FeedFormat) -> @debug.Repr {
  @debug.Debug::to_repr(self)
}

///|
pub(all) suberror DetectError {
  EmptyInput
  UnknownFormat
  MalformedPrefix(String)
} derive(Eq, @debug.Debug)

///|
pub fn DetectError::equal(self : DetectError, other : DetectError) -> Bool {
  Eq::equal(self, other)
}

///|
pub fn DetectError::not_equal(self : DetectError, other : DetectError) -> Bool {
  !Eq::equal(self, other)
}

///|
pub fn DetectError::to_repr(self : DetectError) -> @debug.Repr {
  @debug.Debug::to_repr(self)
}

///|
fn xml_whitespace(value : String) -> Bool {
  let mut is_space = true
  for ch in value.to_array() {
    if ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n' {
      is_space = false
    }
  }
  is_space
}

///|
fn classify_root(name : @xmlmini.ExpandedName) -> FeedFormat raise DetectError {
  match (name.namespace_uri, name.local_name) {
    (None, "rss") => Rss
    (Some("http://www.w3.org/2005/Atom"), "feed" | "entry") => Atom
    _ => raise UnknownFormat
  }
}

///|
/// Lightly classify the input prefix. Full syntax and format validation is left to each parser.
pub fn detect_format(input : String) -> FeedFormat raise DetectError {
  let normalized = if input.has_prefix("\u{FEFF}") {
    input["\u{FEFF}".length():].to_owned()
  } else {
    input
  }
  let prefix = normalized.trim_start(chars=" \t\r\n")
  if prefix.is_empty() {
    raise EmptyInput
  }
  if prefix.has_prefix("{") {
    return JsonFeed
  }
  if !prefix.has_prefix("<") {
    raise UnknownFormat
  }
  let reader = @xmlmini.NamespaceReader::from_string(normalized)
  while true {
    let event = reader.read_event() catch {
      _ => raise MalformedPrefix("malformed XML namespace or root tag")
    }
    match event {
      @xmlmini.NamespaceText(value) if xml_whitespace(value) => ()
      @xmlmini.NamespaceStart(element) | @xmlmini.NamespaceEmpty(element) =>
        return classify_root(element.name)
      @xmlmini.NamespaceEof => raise EmptyInput
      _ => raise MalformedPrefix("expected an XML document element")
    }
  } nobreak {
    raise EmptyInput
  }
}