revq 0.1.1

Review and query your pull requests
use anyhow::{Context, Result};
use serde_json::Value;
use skim::prelude::*;
use std::io::Cursor;

#[derive(Clone)]
pub struct PR {
    pub title: String,
    pub repo: String,
    pub url: String,
}

pub fn build_pr_list(prs: &Value) -> Result<Vec<PR>> {
    let edges = prs["data"]["search"]["edges"]
        .as_array()
        .context("Failed to parse 'edges' as array")?;

    let pr_list = edges
        .iter()
        .map(|pr| {
            let title = pr["node"]["title"]
                .as_str()
                .unwrap_or("No title")
                .to_string();
            let repo = pr["node"]["repository"]["nameWithOwner"]
                .as_str()
                .unwrap_or("Unknown repo")
                .to_string();
            let url = pr["node"]["url"].as_str().unwrap_or("").to_string();

            Ok(PR { title, repo, url })
        })
        .collect::<Result<Vec<PR>>>()?;

    Ok(pr_list)
}

pub fn select_pr(pr_list: Vec<PR>) -> Option<Vec<PR>> {
    let options = SkimOptionsBuilder::default().multi(true).build().unwrap();

    let input = pr_list
        .iter()
        .map(|pr| format!("{} - {}", pr.repo, pr.title))
        .collect::<Vec<_>>()
        .join("\n");

    let item_reader = SkimItemReader::default();
    let items = item_reader.of_bufread(Cursor::new(input));

    if let Some(out) = Skim::run_with(&options, Some(items)) {
        match out.final_key {
            Key::Enter => {
                let selected_prs: Vec<PR> = out
                    .selected_items
                    .iter()
                    .filter_map(|selected_item| {
                        let selected_output = selected_item.output().to_string();
                        pr_list
                            .iter()
                            .find(|pr| format!("{} - {}", pr.repo, pr.title) == selected_output)
                            .cloned()
                    })
                    .collect();

                return Some(selected_prs);
            }
            Key::ESC | Key::Ctrl('c') | Key::Ctrl('d') => {
                return None;
            }
            _ => {
                return None;
            }
        }
    }

    None
}