///|
fn pad_left(text : String, width : Int) -> String {
  let sb = StringBuilder()
  for _ in 0..<(width - text.length()) {
    sb.write_char(' ')
  }
  sb.write_string(text)
  sb.to_string()
}

///|
fn spaces(width : Int) -> String {
  let sb = StringBuilder()
  for _ in 0.. Unit {
  let scanner = @stream.LineScanner::new(reader)
  while scanner.next() is Some(line) {
    let numbered = match style {
      "a" => true
      "t" => !line.data.is_empty()
      _ => false
    }
    if numbered {
      @stdio.stdout.write(pad_left(number.val.to_string(), width) + separator)
      @stdio.stdout.write(line.data)
      @stdio.stdout.write("\n")
      number.val += 1
    } else {
      @stdio.stdout.write(spaces(width))
      @stdio.stdout.write(line.data)
      @stdio.stdout.write("\n")
    }
  }
}

///|
async fn main {
  let args = @env.args()[1:]
  let parsed = @cli.parse(args, [
    @cli.option("body-numbering", short='b'),
    @cli.option("number-width", short='w'),
    @cli.option("number-separator", short='s'),
    @cli.flag("help"),
  ]) catch {
    @cli.CliError(option~, message~, ..) => {
      @stdio.stderr.write("nl: \{message}: '\{option}'\n")
      @sys.exit(2)
      return
    }
  }
  if parsed.contains("help") {
    @stdio.stdout.write(
      "Usage: nl [-b STYLE] [-w NUMBER] [-s STRING] [FILE...]\n",
    )
    return
  }
  let style = parsed.last_value("body-numbering").unwrap_or("t")
  if !(style is ("a" | "t" | "n")) {
    @stdio.stderr.write("nl: invalid body numbering style: '\{style}'\n")
    @sys.exit(2)
    return
  }
  let width_text = parsed.last_value("number-width").unwrap_or("6")
  let width = @string.parse_int(width_text) catch {
    _ => {
      @stdio.stderr.write(
        "nl: invalid line number field width: '\{width_text}'\n",
      )
      @sys.exit(2)
      return
    }
  }
  if width < 1 {
    @stdio.stderr.write(
      "nl: invalid line number field width: '\{width_text}'\n",
    )
    @sys.exit(2)
    return
  }
  let separator = parsed.last_value("number-separator").unwrap_or("\t")
  let files = parsed.operands
  let sources = if files.is_empty() { ["-"] } else { files }
  let number = Ref(1)
  let mut failed = false
  for path in sources {
    try {
      if path == "-" {
        process_reader(@stdio.stdin, style, width, separator, number)
      } else {
        let file = @fs.open(path, mode=ReadOnly)
        defer file.close()
        process_reader(file, style, width, separator, number)
      }
    } catch {
      err => {
        @stdio.stderr.write("nl: \{err}\n")
        failed = true
        continue
      }
    }
  }
  if failed {
    @sys.exit(1)
  }
}