use std::path::PathBuf;
use std::time::Duration;
use crate::cli_config::{self, UpdateMode};
const NOTIFY_DRAIN: Duration = Duration::from_millis(800);
const INSTALL_DRAIN: Duration = Duration::from_secs(10);
pub fn options() -> kaishin::KaishinOptions {
kaishin::KaishinOptions::new(
"yukimemi",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"),
)
}
fn cache_state_path() -> Option<PathBuf> {
dirs::cache_dir().map(|d| d.join("kanade").join("last_update_check.json"))
}
type CheckResult = anyhow::Result<Option<kaishin::LatestRelease>>;
pub enum UpdateHandle {
CachedAvailable {
checker: kaishin::Checker,
latest: kaishin::LatestRelease,
},
Pending {
checker: kaishin::Checker,
handle: tokio::task::JoinHandle<CheckResult>,
cached_latest: Option<kaishin::LatestRelease>,
},
Installing {
handle: tokio::task::JoinHandle<CheckResult>,
},
}
pub fn maybe_spawn(skip: bool) -> Option<UpdateHandle> {
if skip {
return None;
}
if std::env::var_os("KANADE_NO_AUTOUPDATE").is_some() {
return None;
}
let cfg = cli_config::load().update;
if cfg.mode == UpdateMode::Off {
return None;
}
let mut checker = kaishin::Checker::new(env!("CARGO_PKG_NAME"), options());
if let Some(p) = cache_state_path() {
checker = checker.state_path(p);
}
if let Some(iv) = cfg
.check_interval
.as_deref()
.and_then(|s| kaishin::parse_interval(s).ok())
{
checker = checker.interval(iv);
}
match cfg.mode {
UpdateMode::Off => None,
UpdateMode::Install => {
let c = checker.clone();
Some(UpdateHandle::Installing {
handle: tokio::spawn(async move { c.auto_update().await }),
})
}
UpdateMode::Notify => {
if checker.should_check() {
let c = checker.clone();
let cached_latest = checker.cached_update();
Some(UpdateHandle::Pending {
checker,
handle: tokio::spawn(async move { c.check_and_save().await }),
cached_latest,
})
} else {
checker
.cached_update()
.map(|latest| UpdateHandle::CachedAvailable { checker, latest })
}
}
}
}
pub async fn finalize(handle: Option<UpdateHandle>) {
match handle {
None => {}
Some(UpdateHandle::CachedAvailable { checker, latest }) => {
eprintln!("\n{}", checker.format_banner(&latest));
}
Some(UpdateHandle::Pending {
checker,
handle,
cached_latest,
}) => {
let latest = match tokio::time::timeout(NOTIFY_DRAIN, handle).await {
Ok(Ok(Ok(latest))) => latest,
_ => cached_latest,
};
if let Some(latest) = latest {
eprintln!("\n{}", checker.format_banner(&latest));
}
}
Some(UpdateHandle::Installing { handle }) => {
if let Ok(Ok(Ok(Some(latest)))) = tokio::time::timeout(INSTALL_DRAIN, handle).await {
eprintln!(
"\nkanade updated to {} — takes effect on the next invocation.",
latest.tag_name
);
}
}
}
}