use anyhow::{Context, Result};
use reqwest::blocking::Client;
use serde::Deserialize;
use std::collections::HashSet;
use std::env;
use std::fs;
use std::path::PathBuf;
use std::time::Duration;
#[derive(Deserialize)]
struct GitHubRepo {
name: String,
#[allow(dead_code)]
description: Option<String>,
#[serde(rename = "html_url")]
#[allow(dead_code)]
url: String,
}
pub fn get_template_suggestions() -> Result<Vec<String>> {
let mut suggestions = HashSet::new();
if let Ok(local_templates) = get_local_templates() {
suggestions.extend(local_templates);
}
if let Ok(github_templates) = get_github_templates() {
suggestions.extend(github_templates);
}
let mut result: Vec<String> = suggestions.into_iter().collect();
result.sort();
Ok(result)
}
fn get_local_templates() -> Result<Vec<String>> {
let home = env::var("HOME").context("HOME environment variable not set")?;
let angreal_cache = PathBuf::from(home).join(".angrealrc");
if !angreal_cache.exists() {
return Ok(Vec::new());
}
let mut templates = Vec::new();
for entry in fs::read_dir(&angreal_cache)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
if let Some(name) = path.file_name() {
if let Some(name_str) = name.to_str() {
if !name_str.starts_with('.') {
templates.push(name_str.to_string());
}
}
}
}
}
Ok(templates)
}
fn get_github_templates() -> Result<Vec<String>> {
let client = Client::builder()
.timeout(Duration::from_millis(500))
.user_agent("angreal-completion")
.build()?;
let url = "https://api.github.com/orgs/angreal/repos?type=public&sort=updated&per_page=50";
let response = client
.get(url)
.send()
.context("Failed to fetch GitHub repositories")?;
if !response.status().is_success() {
return Ok(Vec::new());
}
let repos: Vec<GitHubRepo> = response
.json()
.context("Failed to parse GitHub API response")?;
let mut templates = Vec::new();
for repo in repos {
if is_template_repo(&repo) {
templates.push(repo.name);
}
}
Ok(templates)
}
fn is_template_repo(repo: &GitHubRepo) -> bool {
let name = repo.name.to_lowercase();
!name.starts_with("angreal")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_local_templates() {
let _ = get_local_templates();
}
#[test]
fn test_is_template_repo() {
let template_repo = GitHubRepo {
name: "python-template".to_string(),
description: Some("A Python project template".to_string()),
url: "https://github.com/angreal/python-template".to_string(),
};
assert!(is_template_repo(&template_repo));
let meta_repo = GitHubRepo {
name: "angreal".to_string(),
description: Some("The main angreal repository".to_string()),
url: "https://github.com/angreal/angreal".to_string(),
};
assert!(!is_template_repo(&meta_repo));
}
#[test]
fn test_get_template_suggestions() {
let suggestions = get_template_suggestions().unwrap_or_default();
#[allow(unused_comparisons)]
{
assert!(suggestions.len() >= 0);
}
}
}