///|
async fn main {
  let args = match @env.args() {
    [_, .. args] => args
    [] => []
  }
  CliRequest::parse(args).execute()
}

///|
async fn CliRequest::execute(self : CliRequest) -> Unit raise @core.CliError {
  match self {
    Project(config) => config.execute()
    Upgrade(paths) => ignore(run_upgrade_request(paths))
    Bump(paths) => run_bump_request(paths)
    Evol(paths) => run_evol_request(paths)
    Pin(paths) => run_pin_request(paths)
    Stable => run_install_request(nightly=false)
    Nightly => run_install_request(nightly=true)
    Preferences(Configure) => configure_preferred_defaults()
    Preferences(Show) => show_preferred_defaults()
  }
}

///|
async fn CliConfig::execute(self : CliConfig) -> Unit raise @core.CliError {
  let target = validate_project_location(self.location)
  let (username, settings) = resolve_project_options(self.overrides)
  match target.mode {
    Create => ensure_dir_recursive(target.project_dir)
    Initialization => ()
  }
  let files = @core.render_project_files(target.project_dir, username, settings)
  target.write_files(files)
  println(target.success_message(files))
}

///|
fn ProjectTarget::success_message(
  self : ProjectTarget,
  files : ArrayView[@core.ProjectFile],
) -> String {
  let action = match self.mode {
    Create => "created and initialized"
    Initialization => "initialized"
  }
  let file_names = files.map(f => f.filename).map(emph).join(", ")
  "\{action} \{file_names} in \{emph(self.project_dir)}"
}

///|
fn version_change_message(
  name : String,
  version_before : String,
  version_after : String,
) -> String {
  let colored_before = @chalk.chalk().modifier(Dim).render(version_before)
  guard version_after != version_before else {
    return "\{emph(name)}: \{colored_before} -> \{colored_before}"
  }
  let colored_after = @chalk.chalk().color(Green).render(version_after)
  "\{emph(name)}: \{colored_before} -> \{colored_after}"
}

///|
pub let emph : (String) -> String = s => @chalk.chalk().color(Cyan).render(s)