ghfetch 0.1.0

GitHub stats in the terminal, neofetch-style
use crate::api::client::GhClient;
use crate::cli::RepoOpts;
use crate::data::languages::LanguageBreakdown;
use anyhow::{Context, Result};
use serde::Serialize;

#[derive(Debug, Serialize)]
pub struct RepoProfile {
    pub name: String,
    pub full_name: String,
    pub owner: String,
    pub description: Option<String>,
    pub stars: u32,
    pub forks: u32,
    pub watchers: u32,
    pub open_issues: u32,
    pub size_kb: u32,
    pub default_branch: String,
    pub license: Option<String>,
    pub topics: Vec<String>,
    pub archived: bool,
    pub created: String,
    pub updated: String,
    pub pushed: Option<String>,
    pub url: String,
    pub languages: Option<LanguageBreakdown>,
}

pub async fn fetch_repo_profile(
    client: &GhClient,
    repo_path: &str,
    opts: &RepoOpts,
) -> Result<RepoProfile> {
    let (owner, repo) = repo_path
        .split_once('/')
        .context("Repository must be in owner/repo format")?;

    let detail = client.get_repo_detail(owner, repo).await?;

    let languages = if opts.show_languages() {
        let lang_bytes = client.get_repo_languages(owner, repo).await?;
        let lang_map = std::collections::HashMap::from([(
            repo.to_string(),
            lang_bytes.into_iter().collect::<Vec<_>>(),
        )]);
        let limit = if opts.detailed_languages() { 0 } else { 10 };
        Some(crate::data::languages::aggregate_languages(
            &lang_map, limit,
        ))
    } else {
        None
    };

    Ok(RepoProfile {
        name: detail.name,
        full_name: detail.full_name,
        owner: detail.owner.login,
        description: detail.description,
        stars: detail.stargazers_count,
        forks: detail.forks_count,
        watchers: detail.subscribers_count,
        open_issues: detail.open_issues_count,
        size_kb: detail.size,
        default_branch: detail.default_branch,
        license: detail.license.map(|l| l.name),
        topics: detail.topics.unwrap_or_default(),
        archived: detail.archived,
        created: detail.created_at.format("%b %Y").to_string(),
        updated: detail.updated_at.format("%b %-d, %Y").to_string(),
        pushed: detail.pushed_at.map(|d| d.format("%b %-d, %Y").to_string()),
        url: detail.html_url,
        languages,
    })
}