use std::io::IsTerminal;
use std::path::PathBuf;
use std::time::Duration;
use anyhow::Result;
use crate::config::AutoUpdateMode;
pub async fn run_self_update(yes: bool, check_only: bool) -> Result<()> {
let opts = kaishin::KaishinOptions::new(
"yukimemi",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"),
);
let non_interactive = !std::io::stdin().is_terminal();
let upd_opts = kaishin::UpdateOptions::new()
.yes(yes)
.check_only(check_only)
.non_interactive(non_interactive);
kaishin::run_self_update(&opts, upd_opts).await
}
const FINALIZE_TIMEOUT: Duration = Duration::from_secs(5);
const NO_AUTOUPDATE_ENV: &str = "SHOKA_NO_AUTOUPDATE";
fn kaishin_opts() -> kaishin::KaishinOptions {
kaishin::KaishinOptions::new(
"yukimemi",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"),
)
}
pub fn auto_update_disabled_by_env() -> bool {
match std::env::var(NO_AUTOUPDATE_ENV) {
Ok(v) => {
let t = v.trim();
!t.is_empty() && !t.eq_ignore_ascii_case("0") && !t.eq_ignore_ascii_case("false")
}
Err(_) => false,
}
}
pub enum AutoUpdateHandle {
NotifyCached {
checker: kaishin::Checker,
latest: kaishin::LatestRelease,
},
NotifyPending {
checker: kaishin::Checker,
handle: tokio::task::JoinHandle<Result<Option<kaishin::LatestRelease>>>,
cached_latest: Option<kaishin::LatestRelease>,
},
Installing {
handle: tokio::task::JoinHandle<Result<Option<kaishin::LatestRelease>>>,
},
}
pub fn maybe_spawn_auto_update_check(
mode: AutoUpdateMode,
interval: Duration,
state_path: PathBuf,
) -> Option<AutoUpdateHandle> {
match mode {
AutoUpdateMode::Off => None,
AutoUpdateMode::Notify => {
let checker = kaishin::Checker::new(env!("CARGO_PKG_NAME"), kaishin_opts())
.interval(interval)
.state_path(state_path);
if !checker.should_check() {
return checker
.cached_update()
.map(|latest| AutoUpdateHandle::NotifyCached { checker, latest });
}
let cached_latest = checker.cached_update();
let checker_clone = checker.clone();
let handle = tokio::spawn(async move { checker_clone.check_and_save().await });
Some(AutoUpdateHandle::NotifyPending {
checker,
handle,
cached_latest,
})
}
AutoUpdateMode::Install => {
let checker = kaishin::Checker::new(env!("CARGO_PKG_NAME"), kaishin_opts())
.interval(interval)
.state_path(state_path);
let handle = tokio::spawn(async move { checker.auto_update().await });
Some(AutoUpdateHandle::Installing { handle })
}
}
}
pub async fn finalize_auto_update_check(handle: AutoUpdateHandle) {
match handle {
AutoUpdateHandle::NotifyCached { checker, latest } => {
eprintln!("\n{}", checker.format_banner(&latest));
}
AutoUpdateHandle::NotifyPending {
checker,
handle,
cached_latest,
} => {
match tokio::time::timeout(FINALIZE_TIMEOUT, handle).await {
Ok(Ok(Ok(Some(latest)))) => {
eprintln!("\n{}", checker.format_banner(&latest));
}
Ok(Ok(Ok(None))) => {
}
_ => {
if let Some(latest) = cached_latest {
eprintln!("\n{}", checker.format_banner(&latest));
}
}
}
}
AutoUpdateHandle::Installing { handle } => {
if let Ok(Ok(Ok(Some(latest)))) = tokio::time::timeout(FINALIZE_TIMEOUT, handle).await {
let version = latest.tag_name.trim_start_matches('v');
eprintln!(
"\u{2713} {bin} {version} installed in the background — restart to apply.",
bin = env!("CARGO_PKG_NAME"),
);
}
}
}
}