#[allow(clippy::too_many_arguments)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_analyze_webassembly(
project_path: PathBuf,
format: ComplexityOutputFormat,
include_binary: bool,
include_text: bool,
_memory_analysis: bool,
security: bool,
complexity: bool,
output: Option<PathBuf>,
perf: bool,
) -> Result<()> {
eprintln!("🔍 Analyzing WebAssembly files...");
let start = std::time::Instant::now();
let wasm_files = collect_wasm_files(&project_path, include_binary, include_text)?;
eprintln!("📁 Found {} WebAssembly files", wasm_files.len());
let results = analyze_wasm_files(
wasm_files,
include_binary,
include_text,
security,
complexity,
)
.await;
let elapsed = start.elapsed();
eprintln!("📊 Analysis complete in {:.2}s", elapsed.as_secs_f64());
write_wasm_analysis_output(&results, &format, perf, elapsed, output).await?;
Ok(())
}
async fn analyze_wasm_files(
wasm_files: Vec<PathBuf>,
include_binary: bool,
include_text: bool,
security: bool,
complexity: bool,
) -> Vec<(PathBuf, WasmMetrics)> {
let mut results = Vec::new();
for file_path in wasm_files {
if let Some(result) = analyze_single_wasm_file(
&file_path,
include_binary,
include_text,
security,
complexity,
)
.await
{
results.push(result);
}
}
results
}
async fn analyze_single_wasm_file(
file_path: &Path,
include_binary: bool,
include_text: bool,
security: bool,
complexity: bool,
) -> Option<(PathBuf, WasmMetrics)> {
match file_path.extension().and_then(|s| s.to_str()) {
Some("wasm") if include_binary => analyze_wasm_binary(file_path).await,
Some("wat") if include_text => {
analyze_wat_text(file_path, security, complexity).await;
None }
_ => None,
}
}
async fn analyze_wasm_binary(file_path: &Path) -> Option<(PathBuf, WasmMetrics)> {
let analyzer = WasmBinaryAnalyzer::new();
match analyzer.analyze_file(file_path).await {
Ok(analysis) => {
eprintln!("✅ Analyzed binary: {}", file_path.display());
Some((file_path.to_path_buf(), analysis))
}
Err(e) => {
eprintln!("❌ Failed to analyze {}: {}", file_path.display(), e);
None
}
}
}
async fn analyze_wat_text(file_path: &Path, security: bool, complexity: bool) {
if let Ok(content) = tokio::fs::read_to_string(file_path).await {
let mut parser = WatParser::new();
match parser.parse(&content) {
Ok(ast) => {
eprintln!("✅ Parsed WAT: {}", file_path.display());
process_wat_ast(&ast, file_path, security, complexity);
}
Err(e) => {
eprintln!("❌ Failed to parse {}: {}", file_path.display(), e);
}
}
}
}
fn process_wat_ast(ast: &AstDag, file_path: &Path, security: bool, complexity: bool) {
if complexity {
let complexity_analyzer = WasmComplexityAnalyzer::new();
let _ = complexity_analyzer.analyze_ast(ast);
}
if security {
let security_validator = WasmSecurityValidator::new();
if let Err(e) = security_validator.validate_ast(ast) {
eprintln!("⚠️ Security issue in {}: {}", file_path.display(), e);
}
}
}
async fn write_wasm_analysis_output(
results: &[(PathBuf, WasmMetrics)],
format: &ComplexityOutputFormat,
perf: bool,
elapsed: std::time::Duration,
output: Option<PathBuf>,
) -> Result<()> {
let output_text = format_webassembly_results(results, format, perf, elapsed)?;
if let Some(output_path) = output {
tokio::fs::write(&output_path, &output_text).await?;
eprintln!("📝 Results written to: {}", output_path.display());
} else {
println!("{output_text}");
}
Ok(())
}