summaryrefslogtreecommitdiff
path: root/crates/cli/src/bin.rs
blob: c8ab379411bbc7a6f96ef7445ab57e5e7c40a595 (plain)
use std::str::FromStr;

use argh::FromArgs;
use args::WasmArg;
use color_eyre::eyre::Result;
use log::{debug, info};
use tinywasm::{self, types::WasmValue, Module};

use crate::args::to_wasm_args;
mod args;
mod util;

#[cfg(feature = "wat")]
mod wat;

#[derive(FromArgs)]
/// TinyWasm CLI
struct TinyWasmCli {
    #[argh(subcommand)]
    nested: TinyWasmSubcommand,

    /// log level
    #[argh(option, short = 'l', default = "\"info\".to_string()")]
    log_level: String,
}

#[derive(FromArgs)]
#[argh(subcommand)]
enum TinyWasmSubcommand {
    Run(Run),
}

enum Engine {
    Main,
}

impl FromStr for Engine {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "main" => Ok(Self::Main),
            _ => Err(format!("unknown engine: {}", s)),
        }
    }
}

#[derive(FromArgs)]
/// run a wasm file
#[argh(subcommand, name = "run")]
struct Run {
    /// wasm file to run
    #[argh(positional)]
    wasm_file: String,

    /// function to run
    #[argh(option, short = 'f')]
    func: Option<String>,

    /// arguments to pass to the wasm file
    #[argh(option, short = 'a')]
    args: Vec<WasmArg>,

    /// engine to use
    #[argh(option, short = 'e', default = "Engine::Main")]
    engine: Engine,
}

fn main() -> Result<()> {
    color_eyre::install()?;

    let args: TinyWasmCli = argh::from_env();
    let level = match args.log_level.as_str() {
        "trace" => log::LevelFilter::Trace,
        "debug" => log::LevelFilter::Debug,
        "warn" => log::LevelFilter::Warn,
        "error" => log::LevelFilter::Error,
        "info" => log::LevelFilter::Info,
        _ => log::LevelFilter::Info,
    };

    pretty_env_logger::formatted_builder().filter_level(level).init();

    let cwd = std::env::current_dir()?;

    match args.nested {
        TinyWasmSubcommand::Run(Run {
            wasm_file,
            engine,
            args,
            func,
        }) => {
            debug!("args: {:?}", args);

            let path = cwd.join(wasm_file.clone());
            let module = match wasm_file.ends_with(".wat") {
                #[cfg(feature = "wat")]
                true => {
                    let wat = std::fs::read_to_string(path)?;
                    let wasm = wat::wat2wasm(&wat);
                    tinywasm::Module::parse_bytes(&wasm)?
                }
                #[cfg(not(feature = "wat"))]
                true => return Err(color_eyre::eyre::eyre!("wat support is not enabled in this build")),
                false => tinywasm::Module::parse_file(path)?,
            };

            match engine {
                Engine::Main => run(module, func, to_wasm_args(args)),
            }
        }
    }
}

fn run(module: Module, func: Option<String>, args: Vec<WasmValue>) -> Result<()> {
    let mut store = tinywasm::Store::default();
    let instance = module.instantiate(&mut store, None)?;

    if let Some(func) = func {
        let func = instance.exported_func_by_name(&store, &func)?;
        let res = func.call(&mut store, &args)?;
        info!("{res:?}");
    }

    Ok(())
}