ghpending 0.1.1

CLI to watch GitHub repos for open issues and pull requests at a glance
use std::time::Duration;

use anyhow::Result;
use indicatif::{ProgressBar, ProgressStyle};
use octocrab::Octocrab;
use tokio::time::timeout;

use crate::github::{RepoResult, RepoStatus};
use crate::{config, display, github};

const FETCH_TIMEOUT: Duration = Duration::from_secs(30);

pub async fn run(crab: &Octocrab) -> Result<()> {
    let cfg = config::load()?;

    if cfg.repos.is_empty() {
        println!("No repos tracked. Run `ghpending add` to get started.");
        return Ok(());
    }

    let spinner = ProgressBar::new_spinner();
    spinner.set_style(
        ProgressStyle::default_spinner()
            .template("{spinner:.cyan} {msg}")
            .unwrap_or_else(|_| ProgressStyle::default_spinner()),
    );
    spinner.set_message("Fetching…");
    spinner.enable_steady_tick(Duration::from_millis(100));

    let futures: Vec<_> = cfg
        .repos
        .iter()
        .map(|repo| {
            let repo = repo.clone();
            async move {
                match timeout(FETCH_TIMEOUT, github::fetch_repo_items(crab, &repo)).await {
                    Ok(result) => result,
                    Err(_) => RepoResult {
                        repo: repo.clone(),
                        status: RepoStatus::Error("timeout after 30s".into()),
                    },
                }
            }
        })
        .collect();

    let results = futures::future::join_all(futures).await;

    spinner.finish_and_clear();

    let digest = display::render_digest(&results);
    print!("{digest}");

    Ok(())
}