use std::collections::{HashMap, HashSet};
use std::process::exit;
#[derive(Debug, Clone)]
struct Task {
name: String,
command: String,
description: String,
runner: String,
depends_on: Vec<String>,
pre_hook: Option<String>,
post_hook: Option<String>,
}
fn main() {
println!("🚀 Flux WASM v0.5.0 - Task Runner (WebAssembly)");
println!("⚠️ Sadeleştirilmiş versiyon - Watch mode ve paralel çalıştırma yok");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 || args[1] == "help" || args[1] == "--help" || args[1] == "-h" {
show_help();
exit(0);
}
let task_input = &args[1];
match task_input.as_str() {
"runners" => {
show_runners();
exit(0);
}
"graph" => {
show_dependency_graph();
exit(0);
}
_ => {
let success = run_task_with_dependencies(task_input, &[], &mut HashSet::new());
exit(if success { 0 } else { 1 });
}
}
}
fn show_help() {
println!("Kullanım: flux <task>");
println!("");
println!("Komutlar:");
println!(" flux help - Bu yardım mesajı");
println!(" flux runners - Tüm runner'ları listele");
println!(" flux graph - Dependency graph göster");
println!(" flux <task> - Task'i çalıştır");
println!("");
println!("⚠️ WASM kısıtlamaları:");
println!(" - Watch mode yok");
println!(" - Paralel çalıştırma yok");
println!(" - Dry-run modu yok");
println!("");
println!("Config: flux.yaml");
}
fn show_runners() {
let config = match load_config("flux.yaml") {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Config okunamadı: {}", e);
exit(1);
}
};
let mut runners: HashMap<String, Vec<&Task>> = HashMap::new();
for task in config.values() {
runners.entry(task.runner.clone()).or_default().push(task);
}
println!("Desteklenen Runner'lar (WASM):");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
for (runner, tasks) in &runners {
println!("\n🔧 {} ({} task)", runner, tasks.len());
for task in tasks {
let deps = if task.depends_on.is_empty() {
String::new()
} else {
format!(" [deps: {}]", task.depends_on.join(", "))
};
println!(" • {} - {}{}", task.name, task.description, deps);
}
}
println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
println!("Toplam: {} runner, {} task", runners.len(), config.len());
}
fn show_dependency_graph() {
let config = match load_config("flux.yaml") {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Config okunamadı: {}", e);
exit(1);
}
};
println!("📊 Dependency Graph (WASM):");
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
for (name, task) in &config {
if !task.depends_on.is_empty() {
println!("\n📦 {}", name);
for dep in &task.depends_on {
println!(" └─> {}", dep);
}
}
}
let independent: Vec<_> = config
.values()
.filter(|t| t.depends_on.is_empty())
.map(|t| t.name.clone())
.collect();
if !independent.is_empty() {
println!("\n🎯 Bağımsız task'ler:");
for name in independent {
println!(" • {}", name);
}
}
println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
}
fn run_task_with_dependencies(
task_name: &str,
_args: &[String],
executed: &mut HashSet<String>,
) -> bool {
if executed.contains(task_name) {
return true;
}
let config = match load_config("flux.yaml") {
Ok(c) => c,
Err(e) => {
eprintln!("❌ Config okunamadı: {}", e);
exit(1);
}
};
let task = match config.get(task_name) {
Some(t) => t.clone(),
None => {
eprintln!("❌ Hata: Bilinmeyen task '{}'", task_name);
exit(1);
}
};
if !task.depends_on.is_empty() {
println!("📋 '{}' bağımlılıkları: {:?}", task_name, task.depends_on);
for dep in &task.depends_on {
let success = run_task_with_dependencies(dep, _args, executed);
if !success {
return false;
}
}
}
if !executed.contains(task_name) {
let success = run_single_task_internal(&task);
executed.insert(task_name.to_string());
return success;
}
true
}
fn run_single_task_internal(task: &Task) -> bool {
println!("");
println!("📝 Task: {}", task.name);
println!("🔧 Runner: {}", task.runner);
println!("📖 Açıklama: {}", task.description);
if !task.depends_on.is_empty() {
println!("🔗 Dependencies: {}", task.depends_on.join(", "));
}
println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
if let Some(ref pre_hook) = task.pre_hook {
println!("⚡ Pre-hook: {}", pre_hook);
println!(" (WASM: Pre-hook bilgi amaçlı gösterildi)");
}
println!("→ Command: {}", task.command);
println!(" (WASM: Komut çalıştırma WASI runtime tarafından yapılmalı)");
if let Some(ref post_hook) = task.post_hook {
println!("⚡ Post-hook: {}", post_hook);
println!(" (WASM: Post-hook bilgi amaçlı gösterildi)");
}
println!("✅ Task başarıyla tamamlandı: {}", task.name);
true
}
fn load_config(path: &str) -> Result<HashMap<String, Task>, Box<dyn std::error::Error>> {
let content = std::fs::read_to_string(path)?;
let mut tasks = HashMap::new();
let mut current_task: Option<String> = None;
let mut current_command = String::new();
let mut current_description = String::new();
let mut current_runner = String::from("shell");
let mut current_depends_on: Vec<String> = Vec::new();
let mut current_pre_hook: Option<String> = None;
let mut current_post_hook: Option<String> = None;
for line in content.lines() {
let trimmed = line.trim();
if trimmed.ends_with(':')
&& !trimmed.starts_with(' ')
&& !trimmed.starts_with('\t')
&& !trimmed.starts_with("command:")
&& !trimmed.starts_with("description:")
&& !trimmed.starts_with("runner:")
&& !trimmed.starts_with("depends_on:")
&& !trimmed.starts_with("pre_hook:")
&& !trimmed.starts_with("post_hook:")
{
if let Some(name) = current_task.take() {
tasks.insert(
name.clone(),
Task {
name,
command: current_command.trim().to_string(),
description: current_description.trim().to_string(),
runner: current_runner.clone(),
depends_on: current_depends_on.clone(),
pre_hook: current_pre_hook.clone(),
post_hook: current_post_hook.clone(),
},
);
}
current_task = Some(trimmed.trim_end_matches(':').to_string());
current_command.clear();
current_description.clear();
current_runner = String::from("shell");
current_depends_on.clear();
current_pre_hook = None;
current_post_hook = None;
} else if let Some(ref _name) = current_task {
if trimmed.starts_with("command:") {
current_command = extract_value(trimmed, "command:");
} else if trimmed.starts_with("description:") {
current_description = extract_value(trimmed, "description:");
} else if trimmed.starts_with("runner:") {
current_runner = extract_value(trimmed, "runner:");
} else if trimmed.starts_with("depends_on:") {
let deps_str = extract_value(trimmed, "depends_on:");
let cleaned = deps_str.replace("[", "").replace("]", "").replace("\"", "");
current_depends_on = cleaned
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
} else if trimmed.starts_with("pre_hook:") {
current_pre_hook = Some(extract_value(trimmed, "pre_hook:"));
} else if trimmed.starts_with("post_hook:") {
current_post_hook = Some(extract_value(trimmed, "post_hook:"));
}
}
}
if let Some(name) = current_task {
tasks.insert(
name.clone(),
Task {
name,
command: current_command.trim().to_string(),
description: current_description.trim().to_string(),
runner: current_runner,
depends_on: current_depends_on,
pre_hook: current_pre_hook,
post_hook: current_post_hook,
},
);
}
Ok(tasks)
}
fn extract_value(line: &str, prefix: &str) -> String {
line.splitn(2, prefix)
.nth(1)
.unwrap_or("")
.trim()
.trim_matches('"')
.trim_matches('\'')
.to_string()
}