use std::ffi::OsStr; use std::io::{Read, Write}; use std::path::Path; use eyre::{Context, Result, bail}; use tinywasm::Module; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum InputFormat { Wasm, Wat, Twasm, } pub struct LoadedModule { pub module: Module, pub format: InputFormat, } pub fn load_module(input: &str) -> Result { let bytes = read_input_bytes(input)?; load_module_from_bytes(input, &bytes) } pub fn load_compilable_module(input: &str) -> Result { let loaded = load_module(input)?; if loaded.format == InputFormat::Twasm { bail!("input is already a twasm archive; use `run`, `dump`, or `inspect` instead"); } Ok(loaded.module) } pub fn default_twasm_output_path(input: &str) -> Result { if input == "-" { bail!("--output is required when compiling from stdin"); } let path = Path::new(input); let stem = path.file_stem().and_then(OsStr::to_str).unwrap_or("module"); let output = path.with_file_name(format!("{stem}.twasm")); Ok(output.to_string_lossy().into_owned()) } pub fn write_output_bytes(output: &str, bytes: &[u8], force: bool) -> Result<()> { if output == "-" { std::io::stdout().write_all(bytes)?; std::io::stdout().flush()?; return Ok(()); } let path = Path::new(output); if path.exists() && !force { bail!("output file already exists: {output}; pass --force to overwrite"); } std::fs::write(path, bytes).with_context(|| format!("failed to write output file `{output}`"))?; Ok(()) } fn load_module_from_bytes(input: &str, bytes: &[u8]) -> Result { if bytes.starts_with(b"TWAS") { let module = Module::try_from_twasm(bytes).with_context(|| format!("failed to read twasm input `{input}`"))?; return Ok(LoadedModule { module, format: InputFormat::Twasm }); } #[cfg(feature = "wat")] if input != "-" && has_extension(input, "wat") { let wasm = wat::parse_bytes(bytes).with_context(|| format!("failed to parse WAT input `{input}`"))?; let module = tinywasm::parse_bytes(&wasm).with_context(|| format!("failed to parse Wasm generated from `{input}`"))?; return Ok(LoadedModule { module, format: InputFormat::Wat }); } #[cfg(not(feature = "wat"))] if input != "-" && has_extension(input, "wat") { bail!("wat support is not enabled in this build"); } #[cfg(feature = "wat")] if input == "-" && let Ok(wasm) = wat::parse_bytes(bytes) { let module = tinywasm::parse_bytes(&wasm).context("failed to parse Wasm generated from stdin WAT input")?; return Ok(LoadedModule { module, format: InputFormat::Wat }); } let module = tinywasm::parse_bytes(bytes).with_context(|| format!("failed to parse Wasm input `{input}`"))?; Ok(LoadedModule { module, format: InputFormat::Wasm }) } fn read_input_bytes(input: &str) -> Result> { if input == "-" { let mut bytes = Vec::new(); std::io::stdin().read_to_end(&mut bytes).context("failed to read stdin")?; return Ok(bytes); } std::fs::read(input).with_context(|| format!("failed to read input `{input}`")) } fn has_extension(path: &str, extension: &str) -> bool { Path::new(path).extension().and_then(OsStr::to_str) == Some(extension) }