#[cfg(feature = "cli")]
use clap::{Parser, Subcommand};
use wasmgo::{CompileConfig, OptimizationLevel, Plugin, TargetType, WasmGoPlugin};
#[cfg(feature = "cli")]
#[derive(Parser)]
#[command(name = "wasmgo")]
#[command(about = "Go WebAssembly plugin for Wasmrun")]
#[command(version)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[cfg(feature = "cli")]
#[derive(Subcommand)]
enum Commands {
#[command(alias = "r")]
Run {
#[arg(short, long, default_value = ".", value_name = "PATH")]
project: String,
#[arg(short, long, default_value = "./dist", value_name = "DIR")]
output: String,
#[arg(long, value_enum, default_value = "release")]
optimization: CliOptimization,
#[arg(short, long)]
verbose: bool,
},
#[command(alias = "c")]
Compile {
#[arg(short, long, default_value = ".", value_name = "PATH")]
project: String,
#[arg(short, long, default_value = "./dist", value_name = "DIR")]
output: String,
#[arg(long, value_enum, default_value = "release")]
optimization: CliOptimization,
#[arg(long, value_enum, default_value = "wasm")]
target: CliTarget,
#[arg(short, long)]
verbose: bool,
},
#[command(alias = "check")]
Inspect {
#[arg(short, long, default_value = ".", value_name = "PATH")]
project: String,
},
CanHandle {
#[arg(value_name = "PATH")]
project: String,
},
CheckDeps,
Clean {
#[arg(value_name = "PATH")]
project: String,
},
Info,
Frameworks,
}
#[cfg(feature = "cli")]
#[derive(clap::ValueEnum, Clone, Debug)]
enum CliOptimization {
Debug,
Release,
Size,
}
#[cfg(feature = "cli")]
#[derive(clap::ValueEnum, Clone, Debug)]
enum CliTarget {
Wasm,
WebApp,
}
#[cfg(feature = "cli")]
impl From<CliOptimization> for OptimizationLevel {
fn from(opt: CliOptimization) -> Self {
match opt {
CliOptimization::Debug => OptimizationLevel::Debug,
CliOptimization::Release => OptimizationLevel::Release,
CliOptimization::Size => OptimizationLevel::Size,
}
}
}
#[cfg(feature = "cli")]
impl From<CliTarget> for TargetType {
fn from(target: CliTarget) -> Self {
match target {
CliTarget::Wasm => TargetType::Standard,
CliTarget::WebApp => TargetType::WebApp,
}
}
}
#[cfg(feature = "cli")]
fn print_header() {
println!(
"๐น {} v{}",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION")
);
println!(" {}", env!("CARGO_PKG_DESCRIPTION"));
println!();
}
#[cfg(feature = "cli")]
fn check_project_validity(plugin: &WasmGoPlugin, project: &str) -> bool {
if !plugin.can_handle_project(project) {
eprintln!("โ Error: Not a valid Go project");
eprintln!(" Looking for go.mod or .go files in: {project}");
eprintln!(" Make sure you're in a Go project directory");
return false;
}
true
}
#[cfg(feature = "cli")]
fn check_dependencies(plugin: &WasmGoPlugin) -> bool {
let missing_deps = plugin.get_builder().check_dependencies();
if !missing_deps.is_empty() {
eprintln!("โ Missing required dependencies:");
for dep in &missing_deps {
eprintln!(" โข {dep}");
}
eprintln!();
eprintln!("๐ก Installation suggestions:");
if missing_deps.iter().any(|d| d.contains("go")) {
eprintln!(" โข Install Go: https://golang.org/dl/");
}
if missing_deps.iter().any(|d| d.contains("tinygo")) {
eprintln!(" โข Install TinyGo: https://tinygo.org/getting-started/install/");
}
return false;
}
true
}
#[cfg(feature = "cli")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();
let plugin = WasmGoPlugin::new();
match cli.command {
Commands::Run {
project,
output,
optimization,
verbose,
} => {
if verbose {
print_header();
println!("๐ Preparing Go project for execution...");
println!("๐ Project: {project}");
println!("๐ฆ Output: {output}");
println!("๐ฏ Optimization: {optimization:?}");
println!();
}
if !check_project_validity(&plugin, &project) {
std::process::exit(1);
}
if !check_dependencies(&plugin) {
std::process::exit(1);
}
let builder = plugin.get_builder();
let compile_config = CompileConfig {
project_path: project.clone(),
output_directory: output,
verbose,
optimization_level: optimization.into(),
target_type: TargetType::Standard,
};
match builder.compile(&compile_config) {
Ok(result) => {
if verbose {
println!("โ
Project ready for execution!");
println!("๐ฏ Entry point: {}", result.wasm_file_path);
} else {
println!("{}", result.wasm_file_path);
}
}
Err(e) => {
eprintln!("โ Failed to prepare project for execution: {e}");
std::process::exit(1);
}
}
}
Commands::Compile {
project,
output,
optimization,
target,
verbose,
} => {
if verbose {
print_header();
println!("๐จ Compiling Go project to WebAssembly...");
println!("๐ Project: {project}");
println!("๐ฆ Output: {output}");
println!("๐ฏ Optimization: {optimization:?}");
println!("๐๏ธ Target: {target:?}");
println!();
}
if !check_project_validity(&plugin, &project) {
std::process::exit(1);
}
if !check_dependencies(&plugin) {
std::process::exit(1);
}
let builder = plugin.get_builder();
let compile_config = CompileConfig {
project_path: project.clone(),
output_directory: output,
verbose,
optimization_level: optimization.into(),
target_type: target.into(),
};
match builder.compile(&compile_config) {
Ok(result) => {
println!("โ
Compilation completed successfully!");
println!("๐ฏ WASM file: {}", result.wasm_file_path);
if let Some(js_path) = result.js_file_path {
println!("๐ JS bindings: {js_path}");
}
if !result.additional_files.is_empty() {
println!("๐ Additional files: {}", result.additional_files.len());
if verbose {
for file in result.additional_files {
println!(" โข {file}");
}
}
}
}
Err(e) => {
eprintln!("โ Compilation failed: {e}");
std::process::exit(1);
}
}
}
Commands::Inspect { project } => {
print_header();
println!("๐ Inspecting Go project...");
println!();
if plugin.can_handle_project(&project) {
println!("๐ Project Analysis");
println!("โโโโโโโโโโโโโโโโโโโ");
if let Ok(directory_entries) = std::fs::read_dir(&project) {
let go_files: Vec<_> = directory_entries
.filter_map(|entry| entry.ok())
.filter(|entry| {
entry
.path()
.extension()
.map(|extension| extension.to_string_lossy().to_lowercase() == "go")
.unwrap_or(false)
})
.map(|entry| entry.file_name().to_string_lossy().to_string())
.collect();
if !go_files.is_empty() {
println!("๐ Go files: {}", go_files.join(", "));
}
}
if std::path::Path::new(&project).join("go.mod").exists() {
println!("๐ฆ Module: Found go.mod");
}
println!("๐ฏ Type: Go WebAssembly project");
println!("๐ง Build Tool: TinyGo");
println!();
println!("๐ Dependencies");
println!("โโโโโโโโโโโโโโโ");
let missing = plugin.get_builder().check_dependencies();
if missing.is_empty() {
println!("โ
go - Go compiler");
println!("โ
tinygo - WebAssembly compiler for Go");
println!();
println!("๐ Project is ready to compile!");
} else {
for dep in &missing {
println!("โ {dep}");
}
println!();
println!(
"โ ๏ธ Some required dependencies are missing. Install them to proceed."
);
std::process::exit(1);
}
} else {
eprintln!("โ Invalid project: Not a Go project");
eprintln!(" Looking for go.mod or .go files in: {project}");
std::process::exit(1);
}
}
Commands::CanHandle { project } => {
if plugin.can_handle_project(&project) {
println!("โ
Yes, wasmgo can handle this project");
if std::path::Path::new(&project).join("go.mod").exists() {
println!("๐ Found go.mod at: {project}/go.mod");
} else {
println!("๐ Found Go files in: {project}");
}
} else {
println!("โ No, wasmgo cannot handle this project");
println!("๐ Looking for go.mod or .go files in: {project}");
std::process::exit(1);
}
}
Commands::CheckDeps => {
print_header();
println!("๐ Checking system dependencies...");
println!();
let missing = plugin.get_builder().check_dependencies();
if missing.is_empty() {
println!("โ
All required dependencies are available!");
println!();
println!("๐ Available tools:");
println!(" โ
go - Go compiler");
println!(" โ
tinygo - WebAssembly compiler for Go");
} else {
println!("โ Missing required dependencies:");
for dep in &missing {
println!(" โข {dep}");
}
println!();
println!("๐ก Installation suggestions:");
println!(" โข Install Go: https://golang.org/dl/");
println!(" โข Install TinyGo: https://tinygo.org/getting-started/install/");
println!(" โข On macOS with Homebrew: brew install go tinygo");
println!(" โข On Ubuntu/Debian: sudo apt install golang-go && follow TinyGo instructions");
std::process::exit(1);
}
}
Commands::Clean { project } => {
println!("๐งน Cleaning project artifacts: {project}");
let dist_path = std::path::Path::new(&project).join("dist");
if dist_path.exists() {
match std::fs::remove_dir_all(&dist_path) {
Ok(_) => println!("โ
Cleaned dist directory"),
Err(e) => println!("โ ๏ธ Failed to clean dist directory: {e}"),
}
}
println!("โ
Project cleaned successfully!");
}
Commands::Info => {
print_header();
println!("๐ง Plugin Information");
println!("โโโโโโโโโโโโโโโโโโโโโ");
let plugin_info = plugin.info();
println!("Name: {}", plugin_info.name);
println!("Version: {}", plugin_info.version);
println!("Description: {}", plugin_info.description);
println!("Author: {}", plugin_info.author);
println!();
println!("๐ฏ Capabilities");
println!("โโโโโโโโโโโโโโโ");
println!("โ
Standard WASM compilation");
println!("โ
TinyGo integration");
println!("โ
Multiple optimization levels");
println!("โ
Go module support");
println!();
println!("๐ Usage");
println!("โโโโโโโโ");
println!("Primary (via Wasmrun):");
println!(" wasmrun run ./my-go-project");
println!(" wasmrun compile ./my-project --optimization size");
println!();
println!("Standalone (testing/development):");
println!(" {} run ./my-project", env!("CARGO_PKG_NAME"));
println!(
" {} compile ./my-project --target webapp",
env!("CARGO_PKG_NAME")
);
println!(" {} inspect ./my-project", env!("CARGO_PKG_NAME"));
}
Commands::Frameworks => {
print_header();
println!("๐ Supported Frameworks & Project Types");
println!("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
println!();
println!("๐ฆ Project Types:");
println!(" โข Standard WASM - Basic Go โ WebAssembly compilation via TinyGo");
println!(" โข Web Applications - Full Go web apps compiled to WebAssembly");
println!();
println!("๐ง Build Tools:");
println!(" โข TinyGo - Primary WebAssembly compiler for Go");
println!(" โข go - Standard Go toolchain for dependency management");
println!();
println!("๐ฏ Optimization Levels:");
println!(" โข debug - Fast compilation, debug symbols");
println!(" โข release - Balanced optimization");
println!(" โข size - Smallest possible output");
}
}
Ok(())
}
#[cfg(not(feature = "cli"))]
fn main() {
println!("Wasmrun Go Plugin v{}", env!("CARGO_PKG_VERSION"));
println!("This plugin is designed to be used with the Wasmrun WebAssembly runtime.");
println!("Configuration is stored in Cargo.toml [package.metadata.wasm-plugin] section.");
println!();
println!("Install the CLI feature to use this binary standalone:");
println!(" cargo install wasmgo --features cli");
println!();
println!("Or use with Wasmrun:");
println!(" wasmrun plugin install wasmgo");
println!(" wasmrun run ./my-go-project");
}