use crate::commands::run::{CliInstance, Preloads, RunCommand};
use crate::common::{RunCommon, RunTarget};
use anyhow::{Context, Result};
use std::fs;
use std::io::{self, Read, Write};
use std::path::PathBuf;
use wasmtime::Module;
use wasmtime_wizer::Wizer;
#[derive(clap::Parser)]
#[expect(missing_docs, reason = "inheriting wizer's docs")]
pub struct WizerCommand {
#[command(flatten)]
run: RunCommon,
#[command(flatten)]
wizer: Wizer,
input: PathBuf,
#[command(flatten)]
preloads: Preloads,
#[arg(short = 'o', long)]
output: Option<PathBuf>,
}
enum WizerInfo<'a> {
Core(wasmtime_wizer::ModuleContext<'a>),
#[cfg(feature = "component-model")]
Component(wasmtime_wizer::ComponentContext<'a>),
}
impl WizerCommand {
pub fn execute(mut self) -> Result<()> {
self.run.common.init_logging()?;
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_time()
.enable_io()
.build()?;
runtime.block_on(self.execute_async())
}
async fn execute_async(mut self) -> Result<()> {
if self.run.common.wasm.relaxed_simd_deterministic.is_none() {
self.run.common.wasm.relaxed_simd_deterministic = Some(true);
}
if self.run.common.wasi.cli.is_none() {
self.run.common.wasi.cli = Some(false);
}
let mut wasm = Vec::new();
if self.input.to_str() == Some("-") {
io::stdin()
.read_to_end(&mut wasm)
.context("failed to read input Wasm module from stdin")?;
} else {
wasm = fs::read(&self.input).context("failed to read input Wasm module")?;
}
#[cfg(feature = "wat")]
let wasm = wat::parse_bytes(&wasm)?;
let is_component = wasmparser::Parser::is_component(&wasm);
let mut run = RunCommand {
run: self.run,
argv0: None,
invoke: Some(if is_component {
format!("{}()", self.wizer.get_init_func())
} else {
self.wizer.get_init_func().to_string()
}),
module_and_args: vec![self.input.clone().into()],
preloads: self.preloads.clone(),
};
let engine = run.new_engine()?;
let (cx, main) = if is_component {
#[cfg(feature = "component-model")]
{
let (cx, wasm) = self.wizer.instrument_component(&wasm)?;
(
WizerInfo::Component(cx),
RunTarget::Component(wasmtime::component::Component::new(&engine, &wasm)?),
)
}
#[cfg(not(feature = "component-model"))]
unreachable!();
} else {
let (cx, wasm) = self.wizer.instrument(&wasm)?;
(
WizerInfo::Core(cx),
RunTarget::Core(Module::new(&engine, &wasm)?),
)
};
let (mut store, mut linker) = run.new_store_and_linker(&engine, &main)?;
let instance = run
.instantiate_and_run(&engine, &mut linker, &main, &mut store)
.await?;
let final_wasm = match (cx, instance) {
(WizerInfo::Core(cx), CliInstance::Core(instance)) => {
self.wizer
.snapshot(
cx,
&mut wasmtime_wizer::WasmtimeWizer {
store: &mut store,
instance,
},
)
.await?
}
#[cfg(feature = "component-model")]
(WizerInfo::Component(cx), CliInstance::Component(instance)) => {
self.wizer
.snapshot_component(
cx,
&mut wasmtime_wizer::WasmtimeWizerComponent {
store: &mut store,
instance,
},
)
.await?
}
#[cfg(feature = "component-model")]
(WizerInfo::Core(_) | WizerInfo::Component(_), _) => unreachable!(),
};
match &self.output {
Some(file) => fs::write(file, &final_wasm).context("failed to write output file")?,
None => std::io::stdout()
.write_all(&final_wasm)
.context("failed to write output to stdout")?,
}
Ok(())
}
}