use anyhow::{anyhow, Result};
use std::path::PathBuf;
const DEFAULT_REPO: &str = "Rushit/tonin-templates";
const RELEASE_URL_TEMPLATE: &str =
"https://github.com/{repo}/releases/download/{version}/{tarball}";
fn normalize_repo(repo: &str) -> String {
repo.trim_start_matches("github.com/")
.trim_start_matches("https://github.com/")
.to_string()
}
fn cache_dir(repo: &str, version: &str) -> Result<PathBuf> {
let home = std::env::var("HOME").map_err(|_| anyhow!("HOME not set"))?;
let repo_name = repo.split('/').last().unwrap_or("tonin-templates");
let cache_path = PathBuf::from(home)
.join(".tonin")
.join("templates")
.join(repo_name)
.join(version);
Ok(cache_path)
}
pub async fn download_if_missing(repo: Option<&str>, version: &str) -> Result<PathBuf> {
let repo = repo.unwrap_or(DEFAULT_REPO);
let repo = normalize_repo(repo);
let cache_path = cache_dir(&repo, version)?;
if cache_path.exists() {
eprintln!(
"✓ Using cached templates from {}",
cache_path.display()
);
return Ok(cache_path);
}
let repo_name = repo.split('/').last().unwrap_or("tonin-templates");
let tarball = format!("{}-{}.tar.gz", repo_name, version);
let url = RELEASE_URL_TEMPLATE
.replace("{repo}", &repo)
.replace("{version}", version)
.replace("{tarball}", &tarball);
eprintln!(
"⬇ Downloading templates from {} ...",
url
);
let bytes = reqwest::Client::new()
.get(&url)
.send()
.await
.map_err(|e| anyhow!("Failed to download templates: {}", e))?
.bytes()
.await
.map_err(|e| anyhow!("Failed to read response: {}", e))?;
std::fs::create_dir_all(&cache_path)
.map_err(|e| anyhow!("Failed to create cache dir: {}", e))?;
let tar_reader = std::io::Cursor::new(bytes);
let gz = flate2::read::GzDecoder::new(tar_reader);
let mut archive = tar::Archive::new(gz);
archive
.unpack(&cache_path)
.map_err(|e| anyhow!("Failed to extract templates: {}", e))?;
eprintln!(
"✓ Templates cached to {}",
cache_path.display()
);
Ok(cache_path)
}
pub fn resolve_template_version() -> String {
if let Ok(version) = std::env::var("TONIN_TEMPLATE_VERSION") {
return version;
}
"latest".to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normalize_repo() {
assert_eq!(normalize_repo("Owner/repo"), "Owner/repo");
assert_eq!(normalize_repo("github.com/Owner/repo"), "Owner/repo");
assert_eq!(
normalize_repo("https://github.com/Owner/repo"),
"Owner/repo"
);
}
#[test]
fn test_release_urls() {
let url = RELEASE_URL_TEMPLATE
.replace("{repo}", DEFAULT_REPO)
.replace("{version}", "0.4.0")
.replace("{tarball}", "tonin-templates-0.4.0.tar.gz");
assert_eq!(
url,
"https://github.com/Rushit/tonin-templates/releases/download/0.4.0/tonin-templates-0.4.0.tar.gz"
);
let url = RELEASE_URL_TEMPLATE
.replace("{repo}", "Rushit/tonin-templates-flat")
.replace("{version}", "0.4.0")
.replace("{tarball}", "tonin-templates-flat-0.4.0.tar.gz");
assert_eq!(
url,
"https://github.com/Rushit/tonin-templates-flat/releases/download/0.4.0/tonin-templates-flat-0.4.0.tar.gz"
);
}
}