crates-io-cli 4.0.3

Interact with crates.io from the command-line
use super::error::Error;
use crate::args::OutputKind;
use std::{
    env,
    io::{self, Write},
    ops::Add,
    path::PathBuf,
    time::Duration,
};

use crates_index_diff::Index;
use parking_lot::{Condvar, Mutex};
use prettytable::{format, Table};

fn show_changes(repo_path: PathBuf, output_kind: OutputKind) -> Result<(), Error> {
    std::fs::create_dir_all(&repo_path).map_err(|e| Error::RepositoryDirectory {
        source: e,
        path: repo_path.clone().into(),
    })?;
    let index = Index::from_path_or_cloned(repo_path)?;
    let changes = index.fetch_changes()?;

    match output_kind {
        OutputKind::Human => {
            if !changes.is_empty() {
                let table = {
                    let mut t = Table::new();
                    t.set_titles(row![b -> "Name", b -> "Version", b -> "Kind"]);
                    t.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
                    changes.iter().fold(t, |mut t, c| {
                        let v = &c.versions()[0];
                        t.add_row(row![v.name, v.version, c.to_string()]);
                        t
                    })
                };
                table.print_tty(false)?;
            }
            Ok(())
        }
        OutputKind::Json => {
            let versions: Vec<_> = changes.iter().map(|c| &c.versions()[0]).collect();
            serde_json::to_writer_pretty(io::stdout(), &versions).map_err(Into::into)
        }
    }
}

fn default_repository_dir() -> PathBuf {
    let mut p = env::temp_dir();
    p.push("crates-io-bare-clone_for-cli");
    p
}

fn message_after_timeout(msg: String, d: Duration) {
    std::thread::spawn({
        move || {
            let lock = Mutex::new(());
            let cvar = Condvar::new();
            let mut something_to_wait_on = lock.lock();
            if cvar
                .wait_until(&mut something_to_wait_on, std::time::Instant::now().add(d))
                .timed_out()
            {
                writeln!(std::io::stderr(), "{}", msg).ok();
            }
        }
    });
}

pub fn handle_recent_changes(
    repo_path: Option<PathBuf>,
    output_format: OutputKind,
) -> Result<(), Error> {
    let repo_path = repo_path.unwrap_or_else(default_repository_dir);
    let computation = std::thread::spawn({
        let repo_path = repo_path.clone();
        move || show_changes(repo_path, output_format)
    });
    message_after_timeout(
        format!(
            "Please wait while we check out or fetch the crates.io index at '{path}'",
            path = repo_path.display()
        ),
        Duration::from_secs(3),
    );
    computation.join().unwrap()
}