use crate::result::Result;
use anyhow::anyhow;
use etc::{Etc, FileSystem, Read, Write};
use reqwest::Client;
use std::{env, process::Command};
const GITHUB_TOKEN: &str = "GITHUB_TOKEN";
const GEAR_DAPPS_GH_API: &str = "https://api.github.com/orgs/gear-foundation/repos";
const GEAR_DAPP_ORG: &str = "https://github.com/gear-foundation/";
#[derive(serde::Deserialize)]
struct Repo {
pub name: String,
}
pub async fn list() -> Result<Vec<String>> {
let mut rb = Client::builder()
.user_agent("gcli")
.build()
.map_err(|e| anyhow!("Failed to build http client: {}", e))?
.get(GEAR_DAPPS_GH_API);
if let Ok(tk) = env::var(GITHUB_TOKEN) {
rb = rb.bearer_auth(tk);
}
let resp = rb
.send()
.await
.map_err(|e| anyhow!("Failed to get examples: {}", e))?;
let repos = resp
.json::<Vec<Repo>>()
.await
.map_err(|e| anyhow!("Failed to deserialize example list: {}", e))?
.into_iter()
.map(|repo| repo.name)
.collect();
Ok(repos)
}
pub async fn download(example: &str, path: &str) -> Result<()> {
let url = format!("{GEAR_DAPP_ORG}{example}.git");
Command::new("git")
.args(["clone", &url, path, "--depth=1"])
.status()
.map_err(|e| anyhow!("Failed to download example: {e}"))?;
let repo = Etc::new(path)?;
repo.rm(".git")?;
Command::new("git")
.args(["init", path])
.status()
.map_err(|e| anyhow!("Failed to init git: {e}"))?;
let mut manifests = Vec::new();
repo.find_all("Cargo.toml", &mut manifests)?;
for manifest in manifests {
let manifest = Etc::new(manifest)?;
let mut toml = String::from_utf8_lossy(
&manifest
.read()
.map_err(|_| anyhow!("Failed to read Cargo.toml"))?,
)
.to_string();
process_manifest(&mut toml)?;
manifest.write(toml.as_bytes())?;
}
Ok(())
}
fn process_manifest(manifest: &mut String) -> Result<()> {
let (user, email) = {
let user_bytes = Command::new("git")
.args(["config", "--global", "--get", "user.name"])
.output()?
.stdout;
let user = String::from_utf8_lossy(&user_bytes);
let email_bytes = Command::new("git")
.args(["config", "--global", "--get", "user.email"])
.output()?
.stdout;
let email = String::from_utf8_lossy(&email_bytes);
(user.to_string(), email.to_string())
};
*manifest = manifest.replace(
r#"authors = ["Gear Technologies"]"#,
&format!("authors = [\"{} <{}>\"]", user.trim(), email.trim()),
);
Ok(())
}
#[tokio::test]
async fn list_examples() {
let ls = list().await.expect("Failed to get examples");
assert!(
ls.contains(&"dapp-template".to_string()),
"all templates: {ls:?}"
);
}