///|
/// Re-format, ignoring how the source was written.
///
/// The contract is the printer's: reading the output back gives the same tree.
/// A file that does not parse is reported and left alone -- reformatting
/// something you could not read is how a formatter loses someone's work.
fn cmd_fmt(args : Array[String]) -> Int32 {
  let mut style : @write.Style = Pretty
  let mut width : Int32? = None
  let mut in_place = false
  let files = []
  let mut i = 0
  while i < args.length() {
    match args[i] {
      "--style" =>
        match args.get(i + 1) {
          Some("flat") => {
            style = Flat
            i = i + 2
          }
          Some("pretty") => {
            style = Pretty
            i = i + 2
          }
          Some("armoured") | Some("armored") => {
            style = Armoured
            i = i + 2
          }
          Some("multiline") => {
            style = PreferMultiline
            i = i + 2
          }
          _ => {
            println(
              "shrubbery: --style needs flat, pretty, armoured or multiline",
            )
            return 2
          }
        }
      "--width" =>
        match args.get(i + 1) {
          Some(v) =>
            match parse_width(v) {
              Some(w) => {
                width = Some(w)
                i = i + 2
              }
              None => {
                println("shrubbery: --width needs a number, got '\{v}'")
                return 2
              }
            }
          None => {
            println("shrubbery: --width needs a value")
            return 2
          }
        }
      "--write" | "-w" => {
        in_place = true
        i = i + 1
      }
      arg => {
        files.push(arg)
        i = i + 1
      }
    }
  }
  if files.length() == 0 {
    println("shrubbery: fmt needs at least one file")
    return 2
  }
  let mut bad = false
  for path in files {
    let src = match read(path) {
      Some(s) => s
      None => {
        bad = true
        continue
      }
    }
    let formatted = try
      @write.write(@parser.parse(src).root(), style~, width~) + "\n"
    catch {
      ShrubberyError(d) => {
        println("shrubbery: \{path}: \{d.kind.reference_text()}")
        bad = true
        continue
      }
    }
    if in_place {
      if formatted != src {
        @fs.write_string_to_file(path, formatted) catch {
          _ => {
            println("shrubbery: cannot write \{path}")
            bad = true
          }
        }
      }
    } else {
      print_trimmed(formatted)
    }
  }
  if bad {
    1
  } else {
    0
  }
}

///|
fn parse_width(s : String) -> Int32? {
  if s.length() == 0 {
    return None
  }
  let mut v = 0
  for c in s {
    if c < '0' || c > '9' {
      return None
    }
    v = v * 10 + (c.to_int() - 48)
  }
  Some(v)
}