proofmode 0.9.0

Capture, share, and preserve verifiable photos and videos
Documentation
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);
    }

    // Read and parse the TOML file
    let toml_content = fs::read_to_string(toml_path)?;
    let config: toml::Value = toml::from_str(&toml_content)?;

    // Start building the Makefile 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(),
    ];

    // Collect phony targets
    let mut phony_targets = Vec::new();

    // Convert tasks
    if let Some(tasks) = config.get("tasks").and_then(|t| t.as_table()) {
        let mut task_names: Vec<_> = tasks.keys().collect();
        task_names.sort(); // Sort tasks for consistent output

        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());

                // Get description
                let default_desc = format!("Run {}", task_name);
                let description = task_config
                    .get("description")
                    .and_then(|d| d.as_str())
                    .unwrap_or(&default_desc);

                // Add help entry
                makefile.push(format!("## {}: {}", task_name, description));

                // Build dependencies line
                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);

                // Add command or script
                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()) {
                    // Split multi-line scripts
                    for line in script.lines() {
                        let trimmed = line.trim();
                        // Skip shebang lines
                        if trimmed.starts_with("#!/") {
                            continue;
                        }
                        // Skip empty lines or comments
                        if trimmed.is_empty() || trimmed.starts_with('#') {
                            makefile.push(format!("\t{}", line));
                        } else {
                            makefile.push(format!("\t@{}", line));
                        }
                    }
                }

                makefile.push(String::new()); // Empty line between tasks
            }
        }
    }

    // Add help target
    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());

    // Add clean-all target
    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());

    // Add phony declaration
    phony_targets.push("help".to_string());
    phony_targets.push("clean-all".to_string());
    makefile.push(format!(".PHONY: {}", phony_targets.join(" ")));

    // Special handling for common convenience targets
    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());

    // Write to output file
    let content = makefile.join("\n");
    fs::write(output_path, content)?;

    println!(
        "Generated {} from {}",
        output_path.display(),
        toml_path.display()
    );

    Ok(())
}