aven 0.1.12

Local-first task manager CLI and sync server
Documentation
use std::sync::{Arc, atomic::AtomicBool};

use anyhow::Result;

use crate::cli::SelfUpdateArgs;
use crate::update::{self, CheckOutcome, Release};

pub(crate) async fn run(args: SelfUpdateArgs) -> Result<()> {
    let client = update::client()?;
    println!("Checking for aven updates...");
    let (release, cached) = match update::check_for_update(&client, true).await {
        Ok(CheckOutcome::Current { version, cached }) => {
            println!(
                "aven v{} is current (latest release v{version}){}",
                update::CURRENT_VERSION,
                cached_suffix(cached)
            );
            return Ok(());
        }
        Ok(CheckOutcome::Available { release, cached }) => (release, cached),
        Err(error) => match update::cached_update() {
            Some(release) => (release, true),
            None => return Err(error),
        },
    };

    install_release(client, release, cached, args.yes).await
}

async fn install_release(
    client: reqwest::Client,
    release: Release,
    cached: bool,
    yes: bool,
) -> Result<()> {
    let plan = update::install_plan(release.clone());
    if let Some(lines) = plan.guidance() {
        for line in lines {
            println!("{line}");
        }
        if cached {
            println!("Release information came from the local cache.");
        }
        return Ok(());
    }

    let target = plan
        .direct_target()
        .expect("direct update plan must have a target");
    if !yes {
        println!(
            "aven v{} can update to v{} at {}{}",
            update::CURRENT_VERSION,
            release.version,
            target.display(),
            cached_suffix(cached)
        );
        println!("Run `aven update --yes` to install it.");
        return Ok(());
    }

    let (progress_tx, mut progress_rx) = update::progress_channel();
    let cancelled = Arc::new(AtomicBool::new(false));
    let install = update::install_direct(client, plan, progress_tx, cancelled);
    tokio::pin!(install);
    let mut phase = progress_rx.borrow().phase;
    println!("{}...", phase.label());

    let success = loop {
        tokio::select! {
            result = &mut install => break result?,
            changed = progress_rx.changed() => {
                if changed.is_err() {
                    continue;
                }
                let next = progress_rx.borrow().phase;
                if next != phase {
                    phase = next;
                    println!("{}...", phase.label());
                }
            }
        }
    };

    println!("Installed aven v{}.", success.version);
    println!("Restart any running aven processes to use the update.");
    Ok(())
}

fn cached_suffix(cached: bool) -> &'static str {
    if cached {
        " using cached release information"
    } else {
        ""
    }
}