use anyhow::{Context, Result};
use etc::{Etc, FileSystem, Read, Write};
use reqwest::Client;
use std::{env, path::Path, 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()
.context("failed to build http client")?
.get(GEAR_DAPPS_GH_API);
if let Ok(tk) = env::var(GITHUB_TOKEN) {
rb = rb.bearer_auth(tk);
}
let resp = rb.send().await.context("failed to get examples")?;
let repos = resp
.json::<Vec<Repo>>()
.await
.context("failed to deserialize example list")?
.into_iter()
.map(|repo| repo.name)
.collect();
Ok(repos)
}
pub async fn download(example: &str, path: &Path) -> Result<()> {
let url = format!("{GEAR_DAPP_ORG}{example}.git");
Command::new("git")
.args(["clone", "--depth=1", &url])
.arg(path)
.status()
.context("failed to download example")?;
let repo = Etc::new(path)?;
repo.rm(".git")?;
Command::new("git")
.arg("init")
.arg(path)
.status()
.context("failed to init git")?;
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().context("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:?}"
);
}