use std::process::{Command, Stdio}; use std::{ env, fs, path::{Path, PathBuf}, }; use crate::templates::{Dsa, Dsc, Template}; mod templates; /// Run a command and exit on failure. fn run(cmd: &mut Command) { let status = cmd.status().expect("failed to execute command"); if !status.success() { std::process::exit(1); } } fn main() { // Very small CLI – only three sub‑commands. let args: Vec = env::args().collect(); if args.len() < 2 { eprintln!("Usage: dsx-build [options]"); std::process::exit(1); } match args[1].as_str() { "new" => cmd_new(&args[2..]), "build" => cmd_build(), "package" => todo!("Package manager stub – not implemented yet."), _ => { eprintln!("Unknown command: {}", args[1]); std::process::exit(1); } } } // ---------- new project ---------------------------------------------------- fn cmd_new(args: &[String]) { let mut lang = "dsa"; for i in 0..args.len() { if args[i] == "--lang" && i + 1 < args.len() { lang = &args[i + 1]; } } let lib = args.contains(&"--lib".to_string()); // Determine project root: a subdirectory named after the supplied --name argument. let mut name_opt = None; for i in 0..args.len() { if args[i] == "--name" && i + 1 < args.len() { name_opt = Some(&args[i + 1]); break; } } let project_name = match name_opt { Some(name) => name.to_string(), None => { eprintln!("Error: --name argument required"); std::process::exit(1); } }; let cwd = env::current_dir().unwrap(); let src_path = cwd.join(&project_name).join("src"); fs::create_dir_all(&src_path).expect("Failed to create project directory"); match lang { "dsa" => { // Minimal DSA binary template. let path = src_path.join(format!("main.dsa")); let template = Dsa::create(&project_name, lib); fs::write(path, template).expect("Unable to write DSA file"); } "dsc" => { let path = src_path.join(format!("main.dsc")); let template = Dsc::create(&project_name, lib); fs::write(path, template).expect("Unable to write DSC file"); } _ => { eprintln!("Unsupported language: {}", lang); std::process::exit(1); } } fs::create_dir_all(src_path.join("lib")).expect("Failed to create lib directory"); fs::write( src_path.join("lib/print.dsa"), templates::create_print_lib(), ) .expect("Failed to create print.dsa"); fs::write( src_path.join("lib/maths.dsa"), templates::create_maths_lib(), ) .expect("Failed to create maths.dsa"); println!( "Created new {} project in {}.", lang, src_path.parent().unwrap().display() ); } // ---------- build ---------------------------------------------------------- fn cmd_build() { let cwd = env::current_dir().unwrap(); // Detect .dsc or .dsa files in current directory. let mut has_dsc = false; let mut has_dsa = false; for entry in fs::read_dir(&cwd.join("src")).expect("unable to read dir") { if let Ok(entry) = entry { let path = entry.path(); if path.extension().and_then(|s| s.to_str()) == Some("dsc") { has_dsc = true; } else if path.extension().and_then(|s| s.to_str()) == Some("dsa") { has_dsa = true; } } } if !has_dsc && !has_dsa { eprintln!("No .dsc or .dsa source found in src directory."); std::process::exit(1); } // Assemble main.dsa to a dsb binary. println!("Assembling Project to a DSB binary..."); let build_dir = cwd.join("build"); fs::create_dir_all(&build_dir).expect("Failed to create build directory"); // Copy everything from `cwd/src` to the build directory. fn copy_recursively(src: &Path, dst: &Path) { if src.is_file() { fs::create_dir_all(dst.parent().unwrap()) .expect("Failed to create parent directory"); fs::copy(src, dst).expect("Failed to copy file"); } else if src.is_dir() { for entry in fs::read_dir(src).expect("Unable to read source dir") { let entry = entry.expect("Failed to read entry"); let child_src = entry.path(); let child_dst = dst.join(entry.file_name()); copy_recursively(&child_src, &child_dst); } } } let src_dir = cwd.join("src"); if src_dir.exists() { copy_recursively(&src_dir, &build_dir); } // Change current working directory to the build directory. env::set_current_dir(&build_dir).expect("Failed to change to build directory"); if has_dsc { println!("Compiling DSC to DSA..."); fn compile_recursive(path: &Path) { if path.is_dir() { for entry in fs::read_dir(path).expect("unable to read dir") { let entry = entry.expect("failed to read entry"); compile_recursive(&entry.path()); } } else if path.extension().and_then(|s| s.to_str()) == Some("dsc") { let input_path = path; let output_path = path.with_extension("dsa"); compiler::compile_file(&input_path, &output_path).unwrap_or_else(|e| { eprintln!("Failed to compile {:?}: {}", input_path, e); std::process::exit(1); }); } } compile_recursive(&build_dir); } // Replace .dsc with .dsa only in include statements, recursively for each file. let mut sed_cmd = Command::new("bash"); sed_cmd.args(&[ "-c", &format!( "find \"{}\" -type f -name '*.dsa' -exec sed -i '/^include/ s/\\.dsc/.dsa/g' {{}} +", build_dir.display() ), ]); run(&mut sed_cmd); fs::create_dir_all(&cwd.join("artifacts")).expect("Failed to create build directory"); assembler::assemble_file("./main.dsa", "../artifacts/out.dsb").unwrap_or_else(|e| { eprintln!("Failed to assemble {:?}: {}", "./main.dsa", e); std::process::exit(1); }); println!("Build finished. Binary at {}/main.dsb", build_dir.display()); }