runeforge 25.8.0

Blueprint to optimal stack JSON converter - Part of Rune* Ecosystem
use anyhow::Result;
use clap::{Parser, Subcommand};
use std::path::PathBuf;

mod schema;
mod selector;
mod util;

use schema::Blueprint;
use selector::StackSelector;

#[derive(Parser)]
#[command(name = "runeforge")]
#[command(about = "Blueprint to optimal stack JSON converter")]
#[command(version = "3.0.0")]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Generate optimal stack plan from blueprint
    Plan {
        /// Blueprint file path (YAML or JSON)
        #[arg(short = 'f', long = "file")]
        file: PathBuf,

        /// Deterministic seed for reproducible results
        #[arg(long = "seed")]
        seed: Option<u64>,

        /// Output file path (default: stdout)
        #[arg(short = 'o', long = "out")]
        out: Option<PathBuf>,

        /// Strict mode - fail on schema validation errors
        #[arg(long = "strict")]
        strict: bool,
    },
}

fn main() -> Result<()> {
    let cli = Cli::parse();

    match cli.command {
        Commands::Plan { file, seed, out, strict } => {
            handle_plan_command(file, seed, out, strict)
        }
    }
}

fn handle_plan_command(
    file: PathBuf,
    seed: Option<u64>,
    out: Option<PathBuf>,
    strict: bool,
) -> Result<()> {
    // Load blueprint
    let blueprint = load_blueprint(&file, strict)?;
    
    // Generate stack plan
    let mut selector = StackSelector::new(seed.unwrap_or(42));
    let stack = selector.select_stack(&blueprint)?;
    
    // Validate output schema if strict
    if strict {
        schema::validate_stack_schema(&stack)?;
    }
    
    // Serialize to JSON
    let json_output = serde_json::to_string_pretty(&stack)?;
    
    // Output to file or stdout
    match out {
        Some(path) => {
            std::fs::write(path, json_output)?;
        }
        None => {
            println!("{}", json_output);
        }
    }
    
    Ok(())
}

fn load_blueprint(file: &PathBuf, strict: bool) -> Result<Blueprint> {
    let content = std::fs::read_to_string(file)?;
    
    let blueprint: Blueprint = if file.extension().and_then(|s| s.to_str()) == Some("yaml") 
        || file.extension().and_then(|s| s.to_str()) == Some("yml") {
        serde_yaml::from_str(&content)?
    } else {
        serde_json::from_str(&content)?
    };
    
    // Validate blueprint schema if strict
    if strict {
        schema::validate_blueprint_schema(&blueprint)?;
    }
    
    Ok(blueprint)
}