///|
priv suberror CliError {
  Io(String)
  Parse(@moonron.RonError, String)
  Usage(String)
}

///|
fn CliError::to_string(self : CliError) -> String {
  match self {
    Io(message) => "io error: " + message
    Parse(error, source) => @moonron.render_error(source, error)
    Usage(message) => "usage error: " + message
  }
}

///|
fn parse_source(source : StringView) -> @moonron.RonDocument raise CliError {
  @moonron.parse_document(source) catch {
    error => raise Parse(error, source.to_owned())
  }
}

///|
fn with_header(document : @moonron.RonDocument, body : String) -> String {
  let attribute = document.extensions.to_attribute()
  if attribute.length() == 0 {
    body
  } else {
    attribute + "\n" + body
  }
}

///|
fn check_source(source : StringView) -> Unit raise CliError {
  parse_source(source) |> ignore
}

///|
fn format_source(source : StringView) -> String raise CliError {
  let document = parse_source(source)
  with_header(document, @moonron.to_string_pretty(document.value))
}

///|
fn compact_source(source : StringView) -> String raise CliError {
  let document = parse_source(source)
  with_header(document, @moonron.to_string(document.value))
}

///|
fn usage() -> String {
  (
    #|Usage:
    #|  ron check 
    #|  ron format 
    #|  ron compact 
  )
}

///|
fn run_command(args : Array[String]) -> Unit raise CliError {
  if args.length() == 0 || args[0] == "--help" || args[0] == "help" {
    println(usage())
    return
  }
  if args.length() != 2 {
    raise Usage("expected a command and one file path")
  }
  let command = args[0]
  let path = args[1]
  let source = @fs.read_file_to_string(path) catch {
    @fs.IOError(message) => raise Io(message)
  }
  match command {
    "check" => {
      check_source(source)
      println("ok: " + path)
    }
    "format" => println(format_source(source))
    "compact" => println(compact_source(source))
    _ => raise Usage("unknown command " + command)
  }
}

///|
fn main {
  let raw_args = @env.args()
  let args : Array[String] = if raw_args.length() == 0 {
    []
  } else {
    raw_args[1:].to_owned()
  }
  run_command(args) catch {
    error => {
      eprintln(error.to_string())
      @sys.exit(1)
    }
  }
}