///|
fn help_message() -> String {
  let message =
    #|Usage: comm [-123] FILE1 FILE2
    #|
    #|Compare two sorted files line by line, producing three columns:
    #|lines only in FILE1, lines only in FILE2, and lines in both.
    #|
    #|Options:
    #|  -1  Suppress column 1 (lines unique to FILE1).
    #|  -2  Suppress column 2 (lines unique to FILE2).
    #|  -3  Suppress column 3 (lines in both files).
    #|  -h, --help  Show this help message.
    #|
    #|Use '-' as a file name to read stdin.
  message
}

///|
fn split_lines(text : String) -> Array[String] {
  if text is "" {
    return []
  }
  let lines : Array[String] = text.split("\n").map(v => v.to_owned()).collect()
  if text.has_suffix("\n") {
    ignore(lines.pop())
  }
  lines
}

///|
async fn read_source_text(path : String) -> String {
  if path == "-" {
    @stdio.stdin.read_all().text()
  } else {
    @fs.read_file_to_string(path)
  }
}

///|
async fn main {
  let args = @env.args()[1:]
  let mut hide1 = false
  let mut hide2 = false
  let mut hide3 = false
  let files : Array[String] = []
  for arg in args {
    if arg == "-h" || arg == "--help" {
      @stdio.stdout.write(help_message() + "\n")
      return
    }
    if arg.has_prefix("-") && arg != "-" {
      for i in 1.. hide1 = true
          '2' => hide2 = true
          '3' => hide3 = true
          _ => {
            @stdio.stderr.write("comm: unknown option: '\{arg}'\n")
            @sys.exit(2)
            return
          }
        }
      }
    } else {
      files.push(arg)
    }
  }
  if files.length() != 2 {
    @stdio.stderr.write(
      "comm: expected exactly two files\n\n" + help_message() + "\n",
    )
    @sys.exit(2)
    return
  }
  let (lines1, lines2) = (
    split_lines(read_source_text(files[0])),
    split_lines(read_source_text(files[1])),
  ) catch {
    err => {
      @stdio.stderr.write("comm: \{err}\n")
      @sys.exit(1)
      return
    }
  }
  let col2_prefix = if hide1 { "" } else { "\t" }
  let col3_prefix = (if hide1 { "" } else { "\t" }) +
    (if hide2 { "" } else { "\t" })
  let mut i = 0
  let mut j = 0
  while i < lines1.length() && j < lines2.length() {
    let cmp = lines1[i].compare(lines2[j])
    if cmp < 0 {
      if !hide1 {
        @stdio.stdout.write(lines1[i] + "\n")
      }
      i += 1
    } else if cmp > 0 {
      if !hide2 {
        @stdio.stdout.write(col2_prefix + lines2[j] + "\n")
      }
      j += 1
    } else {
      if !hide3 {
        @stdio.stdout.write(col3_prefix + lines1[i] + "\n")
      }
      i += 1
      j += 1
    }
  }
  while i < lines1.length() {
    if !hide1 {
      @stdio.stdout.write(lines1[i] + "\n")
    }
    i += 1
  }
  while j < lines2.length() {
    if !hide2 {
      @stdio.stdout.write(col2_prefix + lines2[j] + "\n")
    }
    j += 1
  }
}