use clap::ArgMatches;
use std::env;
use std::path::Path;
use std::str;
use std::process::Command;
use std::fs;
use crate::capitalize;
use crate::exec_cmd;
use crate::PState;
use crate::PixelContext;
pub fn pixel_run(ctx: &PixelContext, args: &ArgMatches) {
if ctx.cdir_state == PState::NotPixel {
println!("🚫 Not pixel directory.");
return;
}
let cmds = get_cmds(ctx, args, "run");
for cmd in cmds {
println!("🍀 {}", cmd);
exec_cmd(&cmd);
}
}
pub fn pixel_build(ctx: &PixelContext, args: &ArgMatches) {
if ctx.cdir_state == PState::NotPixel {
println!("🚫 Not pixel directory.");
return;
}
let cmds = get_cmds(ctx, args, "build");
for cmd in cmds {
println!("🍀 {}", cmd);
exec_cmd(&cmd);
}
}
fn get_cmds(ctx: &PixelContext, args: &ArgMatches, subcmd: &str) -> Vec<String> {
let mut cmds = Vec::new();
let mod_name = args.get_one::<String>("mod_name").unwrap();
let loname = mod_name.to_lowercase();
let capname = capitalize(mod_name);
let build_type = args.get_one::<String>("build_type").unwrap();
let release = if args.get_flag("release") {
"--release"
} else {
""
};
let webport = args
.get_one::<String>("webport")
.map(|s| s.as_str())
.unwrap_or("8080");
let other_args: Vec<&str> = args
.get_many::<String>("other")
.unwrap_or_default()
.map(|s| s.as_str())
.collect();
let other_part = if other_args.is_empty() {
String::new()
} else {
format!("-- {}", other_args.join(" "))
};
match build_type.as_str() {
"term" | "t" => cmds.push(format!(
"cargo {} -p {} --features term {} {}",
subcmd, mod_name, release, other_part
)),
"wgpu" | "wg" | "g" => cmds.push(format!(
"cargo {} -p {} --features wgpu {} {}",
subcmd, mod_name, release, other_part
)),
"web" | "w" => {
let mut crate_path = "".to_string();
if ctx.cdir_state == PState::PixelProject {
crate_path = ".".to_string();
} else if ctx.cdir_state == PState::PixelRoot {
let cpath = Path::new("apps").join(mod_name);
if cpath.exists() {
crate_path = cpath.to_string_lossy().to_string();
}
}
env::set_var("RUSTFLAGS", r#"--cfg getrandom_backend="wasm_js""#);
let mut wasm_cmd = Command::new("wasm-pack");
wasm_cmd.args(["build", "--target", "web", &crate_path]);
if !release.is_empty() {
wasm_cmd.arg(release);
}
if let Some(other_args) = args.get_many::<String>("other") {
for arg in other_args {
wasm_cmd.arg(arg);
}
}
println!("🍀 Executing: {:?}", wasm_cmd);
let output = wasm_cmd.output().expect("Failed to execute wasm-pack");
if !output.status.success() {
eprintln!("❌ wasm-pack failed:");
eprintln!("{}", String::from_utf8_lossy(&output.stderr));
return Vec::new();
}
println!("✅ wasm-pack build completed successfully");
let tmpwd_path = Path::new("tmp").join(format!("web_{}", mod_name));
if tmpwd_path.exists() {
if let Err(e) = fs::remove_dir_all(&tmpwd_path) {
eprintln!("Warning: Failed to remove directory {:?}: {}", tmpwd_path, e);
}
}
if let Err(e) = fs::create_dir_all(&tmpwd_path) {
eprintln!("Error: Failed to create directory {:?}: {}", tmpwd_path, e);
return Vec::new();
}
let assets_src = Path::new(&crate_path).join("assets");
let assets_dst = tmpwd_path.join("assets");
if assets_src.exists() {
if let Err(e) = copy_dir_all(&assets_src, &assets_dst) {
eprintln!("Warning: Failed to copy assets: {}", e);
}
}
let rust_pixel_path = Path::new(&ctx.rust_pixel_dir[ctx.rust_pixel_idx]);
let app_pix = assets_dst.join("pix");
if !app_pix.exists() {
let root_pix = rust_pixel_path.join("assets").join("pix");
if root_pix.exists() {
fs::create_dir_all(&assets_dst).ok();
if let Err(e) = copy_dir_all(&root_pix, &app_pix) {
eprintln!("Warning: Failed to copy root pix assets: {}", e);
} else {
println!("📦 Copied shared pix assets from {}", root_pix.display());
}
}
}
let templates_src = rust_pixel_path.join("web-templates");
if let Err(e) = copy_dir_contents(&templates_src, &tmpwd_path) {
eprintln!("Warning: Failed to copy web templates: {}", e);
}
let index_js_path = tmpwd_path.join("index.js");
if index_js_path.exists() {
if let Ok(content) = fs::read_to_string(&index_js_path) {
let content = content
.replace("PixelGame", &format!("{}Game", capname)) .replace("pixel.js", &format!("{}.js", loname)) .replace("\"pixel_game\"", &format!("\"{}\"", loname)); if let Err(e) = fs::write(&index_js_path, content) {
eprintln!("Warning: Failed to update index.js: {}", e);
}
}
}
let pkg_src = Path::new(&crate_path).join("pkg");
if pkg_src.exists() {
let pkg_dst = tmpwd_path.join("pkg");
if let Err(e) = copy_dir_all(&pkg_src, &pkg_dst) {
eprintln!("Warning: Failed to copy pkg: {}", e);
}
}
if subcmd == "run" {
cmds.push(format!("python3 -m http.server -d {} {}", tmpwd_path.display(), webport));
}
}
_ => {}
}
cmds
}
fn copy_dir_all(src: &Path, dst: &Path) -> std::io::Result<()> {
if !src.exists() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("Source directory does not exist: {:?}", src)
));
}
fs::create_dir_all(dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let src_entry = entry.path();
let dst_entry = dst.join(entry.file_name());
if src_entry.is_dir() {
copy_dir_all(&src_entry, &dst_entry)?;
} else {
fs::copy(&src_entry, &dst_entry)?;
}
}
Ok(())
}
fn copy_dir_contents(src: &Path, dst: &Path) -> std::io::Result<()> {
if !src.exists() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("Source directory does not exist: {:?}", src)
));
}
fs::create_dir_all(dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let src_entry = entry.path();
let dst_entry = dst.join(entry.file_name());
if src_entry.is_dir() {
copy_dir_all(&src_entry, &dst_entry)?;
} else {
fs::copy(&src_entry, &dst_entry)?;
}
}
Ok(())
}