use anyhow::{Context, Result};
use colored::Colorize;
use std::path::PathBuf;
use std::process::Command;
use crate::config::{ProjectConfig, template_directory_stack};
use crate::template::{self, TemplateContext};
#[allow(clippy::too_many_lines)]
pub fn generate(config: &ProjectConfig) -> Result<PathBuf> {
let output_dir = std::env::current_dir()?.join(&config.name);
if output_dir.exists() {
anyhow::bail!(
"Directory '{}' already exists. Choose a different name or remove it first.",
output_dir.display()
);
}
let context = TemplateContext::from_config(config);
let template_dirs = template_directory_stack(config);
println!("\n {} {}", "Creating".green().bold(), config.name.bold());
println!(" Templates: {}", template_dirs.join(" + "));
let files = template::collect_template_files(&template_dirs);
if files.is_empty() {
anyhow::bail!(
"No embedded template files found for: {}",
template_dirs.join(", ")
);
}
std::fs::create_dir_all(&output_dir)
.with_context(|| format!("Failed to create: {}", output_dir.display()))?;
let mut count = 0;
for (contents, rel) in &files {
let rel_str = rel
.to_string_lossy()
.replace("{{ crate_name }}", &context.crate_name);
let adjusted_rel = PathBuf::from(rel_str);
template::write_rendered(contents, &adjusted_rel, &output_dir, &context)?;
count += 1;
}
let version_marker = output_dir.join(".grafana-plugin-version");
std::fs::write(&version_marker, env!("CARGO_PKG_VERSION"))
.context("Failed to write version marker")?;
format_generated_files(&output_dir, config.has_wasm);
let git_ok = Command::new("git")
.arg("init")
.current_dir(&output_dir)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.is_ok_and(|s| s.success());
if git_ok {
let _ = Command::new("git")
.args(["add", "."])
.current_dir(&output_dir)
.output();
let _ = Command::new("git")
.args([
"commit",
"-m",
"Initial scaffold from create-grafana-plugin",
])
.current_dir(&output_dir)
.output();
}
println!(" {} Generated {} files", "✓".green().bold(), count);
if git_ok {
println!(" {} Initialized git repository", "✓".green().bold());
} else {
println!(" {} Git init skipped (git not found)", "⚠".yellow().bold());
}
println!("\n {}\n", "Next steps:".bold());
println!(" cd {}", config.name);
if config.has_wasm {
println!(" bun run setup");
} else {
println!(" bun install");
}
println!(" bun run build");
if config.has_docker {
println!(" docker compose up -d");
}
println!(" bun run dev");
println!();
Ok(output_dir)
}
fn format_generated_files(output_dir: &std::path::Path, has_wasm: bool) {
let null = || std::process::Stdio::null();
let run = |cmd: &str, args: &[&str]| -> bool {
Command::new(cmd)
.args(args)
.current_dir(output_dir)
.stdout(null())
.stderr(null())
.status()
.is_ok_and(|s| s.success())
};
print!(" {} Formatting & fixing TS/JS/JSON...", "⟳".cyan().bold());
let biome_ok = run(
"bunx",
&["@biomejs/biome", "check", "--write", "--unsafe", "."],
);
if biome_ok {
println!(
"\r {} Formatted & fixed TS/JS/JSON ",
"✓".green().bold()
);
} else {
println!(
"\r {} TS/JS/JSON formatting skipped (bun not found)",
"⚠".yellow().bold()
);
}
if has_wasm {
print!(" {} Formatting Rust code...", "⟳".cyan().bold());
if run("cargo", &["fmt", "--all"]) {
println!("\r {} Formatted Rust code ", "✓".green().bold());
} else {
println!(
"\r {} Rust formatting skipped (cargo not found)",
"⚠".yellow().bold()
);
}
}
}