summaryrefslogtreecommitdiff
path: root/crates/cli/src/load.rs
diff options
context:
space:
mode:
authorHenry <mail@henrygressmann.de>2026-04-26 19:44:34 +0200
committerHenry <mail@henrygressmann.de>2026-04-26 19:44:34 +0200
commit463a3a6f56a5ca06cda890fd6fe7f16485ef70a0 (patch)
treebfa657a2de16c04cd36f4b6c61e77b370fcd46fc /crates/cli/src/load.rs
parent27004a8738c7a3c32ac4fb6966f32647b4c96db7 (diff)
feat: new tinywasm cli
Signed-off-by: Henry <mail@henrygressmann.de>
Diffstat (limited to 'crates/cli/src/load.rs')
-rw-r--r--crates/cli/src/load.rs103
1 files changed, 103 insertions, 0 deletions
diff --git a/crates/cli/src/load.rs b/crates/cli/src/load.rs
new file mode 100644
index 0000000..d16469b
--- /dev/null
+++ b/crates/cli/src/load.rs
@@ -0,0 +1,103 @@
+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<LoadedModule> {
+ let bytes = read_input_bytes(input)?;
+ load_module_from_bytes(input, &bytes)
+}
+
+pub fn load_compilable_module(input: &str) -> Result<Module> {
+ 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<String> {
+ 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<LoadedModule> {
+ 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<Vec<u8>> {
+ 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)
+}