///|
let version : String = "0.2.0"

///|
let about : String =
  #|Parse, validate, and format TOML files.
  #|
  #|Run it from mooncakes.io without installing (the binary is fetched and
  #|cached on first use; pin a version with bobzhang/toml_cli@):
  #|
  #|  moonx bobzhang/toml_cli check config.toml
  #|  moonx bobzhang/toml_cli format config.toml
  #|  moonx bobzhang/toml_cli tojson config.toml
  #|
  #|Exit codes:
  #|  0  success
  #|  1  the file could not be read or is not valid TOML; a human-readable
  #|     error with position information is printed to stdout
  #|  2  usage error (unknown subcommand or missing file argument)
  #|
  #|`format` prints the normalized document to stdout; it never rewrites the
  #|input file. To format in place, redirect stdout to a temporary file and
  #|move it over the original after checking the exit code.

///|
fn toml_command() -> @argparse.Command {
  Command(
    "toml_cli",
    about~,
    version~,
    arg_required_else_help=true,
    positionals=[
      PositionArg("file", about="Parse TOML and print normalized TOML."),
    ],
    subcommands=[
      Command("format", about="Parse TOML and print normalized TOML.", positionals=[
        PositionArg(
          "file",
          about="TOML file to format.",
          num_args=@argparse.ValueRange::single(),
        ),
      ]),
      Command("check", about="Validate TOML without printing parsed output.", positionals=[
        PositionArg(
          "file",
          about="TOML file to validate.",
          num_args=@argparse.ValueRange::single(),
        ),
      ]),
      Command("tojson", about="Parse TOML and print it as JSON.", positionals=[
        PositionArg(
          "file",
          about="TOML file to convert.",
          num_args=@argparse.ValueRange::single(),
        ),
      ]),
    ],
  )
}

///|
fn main {
  let code = run(@env.args()[1:])
  if code != 0 {
    @sys.exit(code)
  }
}

///|
fn run(args : ArrayView[String]) -> Int {
  let matches = @argparse.parse(toml_command(), argv=args, env=Map([])) catch {
    err => {
      println(err)
      return 2
    }
  }
  match matches.subcommand {
    Some(("format", child)) => dispatch_file(child, format_file)
    Some(("check", child)) => dispatch_file(child, check_file)
    Some(("tojson", child)) => dispatch_file(child, tojson_file)
    Some((name, _)) => {
      println("error: unsupported command `\{name}`")
      2
    }
    None => dispatch_file(matches, format_file)
  }
}

///|
fn dispatch_file(matches : @argparse.Matches, action : (String) -> Int) -> Int {
  match matches.values.get("file") {
    Some([path]) => action(path)
    _ => 2
  }
}

///|
fn format_file(path : String) -> Int {
  let source = @fs.read_file_to_string(path) catch {
    err => {
      println("error: failed to read \{path}: \{err}")
      return 1
    }
  }
  let value = @toml_lib.parse(source) catch {
    err => {
      println("error: failed to parse \{path}: \{err}")
      return 1
    }
  }
  println(value.to_string())
  0
}

///|
fn check_file(path : String) -> Int {
  let source = @fs.read_file_to_string(path) catch {
    err => {
      println("error: failed to read \{path}: \{err}")
      return 1
    }
  }
  let _ = @toml_lib.parse(source) catch {
    err => {
      println("error: failed to parse \{path}: \{err}")
      return 1
    }
  }
  println("\{path}: OK")
  0
}