use colored::Colorize;
use std::process;
#[cfg(feature = "wasm")]
use aver::runtime::wasm_gc as rt;
#[cfg(feature = "wasm")]
use super::shared;
#[cfg(feature = "wasm")]
use super::shared::{parse_file, read_file, resolve_module_root};
#[cfg(feature = "wasm")]
use super::commands::{flatten_multimodule, load_compile_deps};
#[cfg(feature = "wasm")]
pub(super) use rt::EffectMode;
#[cfg(feature = "wasm")]
#[allow(unused_imports)]
pub(super) use rt::RunOutcome;
#[cfg(not(feature = "wasm"))]
#[allow(dead_code)]
pub(super) enum EffectMode {
Normal,
Record,
Replay((), bool),
}
#[cfg(not(feature = "wasm"))]
#[allow(dead_code)]
pub(super) struct RunOutcome {
pub output: (),
pub effects_consumed: usize,
pub effects_total: usize,
pub args_diff_count: usize,
}
pub(super) fn cmd_run_wasm_gc(
file: &str,
module_root_override: Option<&str>,
program_args: Vec<String>,
record_dir: Option<&str>,
entry_expr: Option<&str>,
) {
#[cfg(feature = "wasm")]
{
let mode = match record_dir {
Some(_) => rt::EffectMode::Record,
None => rt::EffectMode::Normal,
};
let entry_info = match entry_expr {
Some(src) => match shared::parse_call_expression(src) {
Ok(info) => Some(info),
Err(e) => {
eprintln!("{}", format!("--expr: {}", e).red());
process::exit(1);
}
},
None => None,
};
cmd_run_wasm_gc_with_mode(
file,
module_root_override,
program_args,
mode,
entry_info,
record_dir,
);
}
#[cfg(not(feature = "wasm"))]
{
let _ = (
file,
module_root_override,
program_args,
record_dir,
entry_expr,
);
eprintln!("{}", "WASM requires --features wasm".red());
process::exit(1);
}
}
#[cfg(feature = "wasm")]
pub(super) fn cmd_run_wasm_gc_with_mode(
file: &str,
module_root_override: Option<&str>,
program_args: Vec<String>,
mode: rt::EffectMode,
entry_info: Option<(String, Vec<aver::value::Value>)>,
record_dir: Option<&str>,
) {
if let Err(e) = try_run_wasm_gc(
file,
module_root_override,
program_args,
mode,
entry_info,
record_dir,
) {
eprintln!("{}", e.red());
process::exit(1);
}
}
#[cfg(feature = "wasm")]
pub(super) fn try_run_wasm_gc(
file: &str,
module_root_override: Option<&str>,
program_args: Vec<String>,
mode: rt::EffectMode,
entry_info: Option<(String, Vec<aver::value::Value>)>,
record_dir: Option<&str>,
) -> Result<rt::RunOutcome, String> {
use aver::ir::{NeutralAllocPolicy, PipelineConfig, TypecheckMode};
let module_root = resolve_module_root(module_root_override);
let source = read_file(file)?;
let mut items = parse_file(&source)?;
let neutral_policy = NeutralAllocPolicy;
let result = aver::ir::pipeline::run(
&mut items,
PipelineConfig {
typecheck: Some(TypecheckMode::Full {
base_dir: Some(&module_root),
}),
alloc_policy: Some(&neutral_policy),
run_interp_lower: false,
run_buffer_build: false,
..Default::default()
},
);
if let Some(tc) = &result.typecheck
&& !tc.errors.is_empty()
{
return Err(shared::format_type_errors(&tc.errors));
}
let dep_modules = load_compile_deps(&items, &module_root, false, false, false);
flatten_multimodule(&mut items, &dep_modules);
aver::ir::pipeline::resolve(&mut items);
let outcome = rt::run_in_process(
&items,
result.analysis.as_ref(),
rt::RunConfig {
program_args,
entry_info: entry_info.clone(),
mode,
},
)?;
if let Some(dir) = record_dir
&& let Some(effects) = outcome.recorded_effects.as_ref()
{
let request_id = super::commands::generate_request_id();
let timestamp = super::commands::generate_timestamp();
let (record_program_file, record_module_root) =
super::commands::recording_paths(file, &module_root);
let file_stem = match &entry_info {
Some((fn_name, args)) => aver::replay::recording_stem(fn_name, args),
None => request_id.clone(),
};
let out_path = super::commands::prepare_recording_path(dir, &file_stem)
.map_err(|e| format!("prepare recording path: {}", e))?;
let entry_fn_label = entry_info
.as_ref()
.map(|(n, _)| n.clone())
.unwrap_or_else(|| "main".to_string());
let input = match &entry_info {
Some((_, args)) => aver::replay::encode_entry_args(args)
.map_err(|e| format!("encode entry args: {}", e))?,
None => aver::replay::JsonValue::Null,
};
let recording = aver::replay::SessionRecording {
schema_version: 1,
request_id,
timestamp,
program_file: record_program_file,
module_root: record_module_root,
entry_fn: entry_fn_label,
input,
effects: effects.clone(),
output: aver::replay::RecordedOutcome::Value(outcome.output.clone()),
};
let json = aver::replay::session_recording_to_string_pretty(&recording);
std::fs::write(&out_path, json)
.map_err(|e| format!("write recording {}: {}", out_path.display(), e))?;
eprintln!("Recorded → {}", out_path.display());
}
Ok(outcome)
}