use std::fs;
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let toml_path = Path::new("Makefile.toml");
let output_path = Path::new("Makefile");
if !toml_path.exists() {
eprintln!("Error: Makefile.toml not found");
std::process::exit(1);
}
let toml_content = fs::read_to_string(toml_path)?;
let config: toml::Value = toml::from_str(&toml_content)?;
let mut makefile = vec![
"# Generated from Makefile.toml".to_string(),
"# Run 'cargo run --bin generate_makefile' to regenerate\n".to_string(),
"SHELL := /bin/bash".to_string(),
".SHELLFLAGS := -ecuo pipefail".to_string(),
".DEFAULT_GOAL := help\n".to_string(),
];
let mut phony_targets = Vec::new();
if let Some(tasks) = config.get("tasks").and_then(|t| t.as_table()) {
let mut task_names: Vec<_> = tasks.keys().collect();
task_names.sort();
for task_name in task_names {
if let Some(task_config) = tasks.get(task_name).and_then(|t| t.as_table()) {
phony_targets.push(task_name.clone());
let default_desc = format!("Run {}", task_name);
let description = task_config
.get("description")
.and_then(|d| d.as_str())
.unwrap_or(&default_desc);
makefile.push(format!("## {}: {}", task_name, description));
let mut deps_line = format!("{}:", task_name);
if let Some(deps) = task_config.get("dependencies").and_then(|d| d.as_array()) {
let dep_names: Vec<String> = deps
.iter()
.filter_map(|d| d.as_str())
.map(|s| s.to_string())
.collect();
if !dep_names.is_empty() {
deps_line.push(' ');
deps_line.push_str(&dep_names.join(" "));
}
}
makefile.push(deps_line);
makefile.push(format!("\t@echo 'Running {}...'", task_name));
if let Some(command) = task_config.get("command").and_then(|c| c.as_str()) {
let args = task_config
.get("args")
.and_then(|a| a.as_array())
.map(|arr| {
arr.iter()
.filter_map(|v| v.as_str())
.collect::<Vec<_>>()
.join(" ")
})
.unwrap_or_default();
let full_command = if args.is_empty() {
command.to_string()
} else {
format!("{} {}", command, args)
};
makefile.push(format!("\t@{}", full_command));
} else if let Some(script) = task_config.get("script").and_then(|s| s.as_str()) {
for line in script.lines() {
let trimmed = line.trim();
if trimmed.starts_with("#!/") {
continue;
}
if trimmed.is_empty() || trimmed.starts_with('#') {
makefile.push(format!("\t{}", line));
} else {
makefile.push(format!("\t@{}", line));
}
}
}
makefile.push(String::new()); }
}
}
makefile.push("## help: Show this help message".to_string());
makefile.push("help:".to_string());
makefile.push("\t@echo 'Available targets:'".to_string());
makefile.push("\t@awk 'BEGIN {FS = \":.*?## \"} /^[a-zA-Z_-]+:.*?## / {printf \" \\033[36m%-20s\\033[0m %s\\n\", $$1, $$2}' $(MAKEFILE_LIST)".to_string());
makefile.push(String::new());
makefile.push("## clean-all: Clean everything".to_string());
makefile.push("clean-all: clean clean-mobile".to_string());
makefile.push("\t@echo 'All cleaned'".to_string());
makefile.push(String::new());
phony_targets.push("help".to_string());
phony_targets.push("clean-all".to_string());
makefile.push(format!(".PHONY: {}", phony_targets.join(" ")));
makefile.push("\n# Convenience targets".to_string());
makefile.push("## all: Build the project".to_string());
makefile.push("all: build".to_string());
makefile.push(String::new());
makefile.push("## check: Run tests".to_string());
makefile.push("check: test".to_string());
makefile.push(String::new());
let content = makefile.join("\n");
fs::write(output_path, content)?;
println!(
"Generated {} from {}",
output_path.display(),
toml_path.display()
);
Ok(())
}