///|
async fn create_link(
  target : String,
  link_path : String,
  force : Bool,
  verbose : Bool,
) -> Bool {
  match @fsops.kind_if_exists(link_path) {
    Some(Directory) =>
      raise @fsops.FsOpError("refusing to replace directory: '\{link_path}'")
    Some(_) if force => @fsops.remove_path(link_path, false)
    Some(_) => raise @fsops.FsOpError("destination exists: '\{link_path}'")
    None => ()
  }
  @fs.symlink(link_path, target~)
  if verbose {
    @stdio.stdout.write("'\{link_path}' -> '\{target}'\n")
  }
  true
}

///|
async fn main {
  let args = @env.args()[1:]
  let parsed = @cli.parse(args, [
    @cli.flag("symbolic", short='s'),
    @cli.flag("force", short='f'),
    @cli.flag("no-target-directory", short='T'),
    @cli.flag("no-target-directory", short='n'),
    @cli.flag("verbose", short='v'),
    @cli.flag("help"),
  ]) catch {
    @cli.CliError(option~, message~, ..) => {
      @stdio.stderr.write("ln: \{message}: '\{option}'\n")
      @sys.exit(2)
      return
    }
  }
  if parsed.contains("help") {
    @stdio.stdout.write("Usage: ln -s [-f] [-T] [-v] TARGET LINK_NAME\n")
    return
  }
  let operands = parsed.operands
  let symbolic = parsed.contains("symbolic")
  let force = parsed.contains("force")
  let no_target_directory = parsed.contains("no-target-directory")
  let verbose = parsed.contains("verbose")
  if !symbolic {
    @stdio.stderr.write(
      "ln: hard links are unavailable in the portable filesystem API; use -s\n",
    )
    @sys.exit(2)
    return
  }
  if operands.length() < 2 {
    @stdio.stderr.write("ln: missing operand\n")
    @sys.exit(1)
    return
  }
  if !@platform.symbolic_links_supported() {
    @stdio.stderr.write("ln: unsupported capability: symbolic links\n")
    @sys.exit(1)
    return
  }
  let destination = operands[operands.length() - 1]
  let targets = operands[0:operands.length() - 1]
  if no_target_directory && targets.length() > 1 {
    @stdio.stderr.write("ln: -T cannot be used with multiple targets\n")
    @sys.exit(1)
    return
  }
  let destination_kind = @fsops.kind_if_exists(destination) catch {
    err => {
      @stdio.stderr.write("ln: cannot inspect '\{destination}': \{err}\n")
      @sys.exit(1)
      return
    }
  }
  if targets.length() > 1 && destination_kind != Some(Directory) {
    @stdio.stderr.write("ln: target is not a directory: '\{destination}'\n")
    @sys.exit(1)
    return
  }
  let mut failed = false
  for target in targets {
    let link_path = if targets.length() > 1 ||
      (!no_target_directory && destination_kind == Some(Directory)) {
      @fsops.join(destination, @fsops.basename(target))
    } else {
      destination
    }
    ignore(
      create_link(target, link_path, force, verbose) catch {
        @fsops.FsOpError(message) => {
          @stdio.stderr.write(
            "ln: failed to create '\{link_path}': \{message}\n",
          )
          failed = true
          false
        }
        err => {
          @stdio.stderr.write("ln: failed to create '\{link_path}': \{err}\n")
          failed = true
          false
        }
      },
    )
  }
  if failed {
    @sys.exit(1)
  }
}