use anyhow::{Context, Result};
use std::io::IsTerminal;
const REPO_OWNER: &str = "hiro-o918";
const REPO_NAME: &str = "rinkaku";
const BIN_NAME: &str = "rinkaku";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ConfirmMode {
Skip,
Prompt,
RefuseNonInteractive,
}
fn confirm_mode(yes: bool, stdin_is_tty: bool) -> ConfirmMode {
if yes {
ConfirmMode::Skip
} else if stdin_is_tty {
ConfirmMode::Prompt
} else {
ConfirmMode::RefuseNonInteractive
}
}
pub fn run_self_update(yes: bool) -> Result<()> {
let no_confirm = match confirm_mode(yes, std::io::stdin().is_terminal()) {
ConfirmMode::Skip => true,
ConfirmMode::Prompt => false,
ConfirmMode::RefuseNonInteractive => {
anyhow::bail!("refusing to self-update non-interactively; pass --yes to proceed");
}
};
let current_version = env!("CARGO_PKG_VERSION");
let target = self_update::get_target();
let status = self_update::backends::github::Update::configure()
.repo_owner(REPO_OWNER)
.repo_name(REPO_NAME)
.bin_name(BIN_NAME)
.target(target)
.current_version(current_version)
.identifier(&release_asset_name(BIN_NAME, target))
.bin_path_in_archive(&archive_bin_path(BIN_NAME, target))
.no_confirm(no_confirm)
.build()?
.update()
.context(
"self-update failed; if this is a permission error, try again with sudo, \
or update via the package manager you installed rinkaku with \
(e.g. `brew upgrade` or `cargo install rinkaku`)",
)?;
if status.updated() {
println!(
"Updated {BIN_NAME} from v{current_version} to {}",
status.version()
);
} else {
println!("{BIN_NAME} is already up to date (v{current_version})");
}
Ok(())
}
fn release_asset_name(bin: &str, target: &str) -> String {
format!("{bin}-{target}.tar.gz")
}
fn archive_bin_path(bin: &str, target: &str) -> String {
format!("{bin}-{target}/{bin}")
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn should_build_dot_tar_gz_asset_name_from_bin_and_target() {
let actual = release_asset_name("rinkaku", "aarch64-apple-darwin");
assert_eq!("rinkaku-aarch64-apple-darwin.tar.gz", actual);
}
#[test]
fn should_build_nested_archive_path_from_bin_and_target() {
let actual = archive_bin_path("rinkaku", "x86_64-unknown-linux-gnu");
assert_eq!("rinkaku-x86_64-unknown-linux-gnu/rinkaku", actual);
}
#[test]
fn should_configure_update_builder_without_error() {
let target = self_update::get_target();
let current_version = env!("CARGO_PKG_VERSION");
let result = self_update::backends::github::Update::configure()
.repo_owner(REPO_OWNER)
.repo_name(REPO_NAME)
.bin_name(BIN_NAME)
.target(target)
.current_version(current_version)
.identifier(&release_asset_name(BIN_NAME, target))
.bin_path_in_archive(&archive_bin_path(BIN_NAME, target))
.no_confirm(true)
.build();
assert!(
result.is_ok(),
"Update builder configuration should succeed"
);
}
#[test]
fn should_skip_confirmation_when_yes_flag_is_set() {
let actual = confirm_mode(true, false);
assert_eq!(ConfirmMode::Skip, actual);
}
#[test]
fn should_prompt_when_yes_flag_is_unset_and_stdin_is_a_tty() {
let actual = confirm_mode(false, true);
assert_eq!(ConfirmMode::Prompt, actual);
}
#[test]
fn should_refuse_non_interactively_when_yes_flag_is_unset_and_stdin_is_not_a_tty() {
let actual = confirm_mode(false, false);
assert_eq!(ConfirmMode::RefuseNonInteractive, actual);
}
#[test]
fn should_skip_confirmation_when_yes_flag_is_set_even_with_a_tty() {
let actual = confirm_mode(true, true);
assert_eq!(ConfirmMode::Skip, actual);
}
}