diff --git a/Cargo.toml b/Cargo.toml index 982724a..e651c09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,15 @@ resolver = "2" members = [ "rusp-lib", "rusp-bin", + "rhai-rusp-lib", + "rhai-rusp-bin", ] [workspace.dependencies] +rhai = { version = "1.16", features = ["serde"] } +rusp-lib = { version = "0.95", path = "rusp-lib" } +rhai-rusp-lib = { version = "0.95", path = "rhai-rusp-lib" } + anyhow = "1.0" serde = "1.0" serde_derive = "1.0" diff --git a/rhai-rusp-bin/Cargo.toml b/rhai-rusp-bin/Cargo.toml new file mode 100644 index 0000000..8b6401c --- /dev/null +++ b/rhai-rusp-bin/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = ["Daniel Egger ", "Axiros GmbH"] +description = "The Rust USP toolkit for Rhai" +edition = "2021" +license = "BSD-3-Clause" +name = "rhai-rusp" +readme = "README.md" +repository = "https://github.com/axiros/rusp" +version = "0.95.0" + +[badges] +[badges.travis-ci] +repository = "axiros/rusp" + +[dependencies] +rhai = { workspace = true } +anyhow = { workspace = true } +clap = { version = "4", features = ["derive"] } +quick-protobuf = { workspace = true } +rusp-lib = { workspace = true } +rhai-rusp-lib = { workspace = true } +serde = { workspace = true } +serde_derive = { workspace = true } +serde_json = { workspace = true } + +[[bin]] +name = "rusp-run" diff --git a/rhai-rusp-bin/src/bin/rusp-run.rs b/rhai-rusp-bin/src/bin/rusp-run.rs new file mode 100644 index 0000000..dff121f --- /dev/null +++ b/rhai-rusp-bin/src/bin/rusp-run.rs @@ -0,0 +1,147 @@ +use clap::Parser; +use rhai::packages::Package; +use rhai::{Engine, EvalAltResult, Position}; +use rhai_rusp_lib::RuspPackage; + +use std::convert::Into; +use std::path::PathBuf; +use std::{fs::File, io::Read, path::Path, process::exit}; + +fn eprint_error(input: &str, mut err: EvalAltResult) { + fn eprint_line(lines: &[&str], pos: Position, err_msg: &str) { + let line = pos.line().unwrap(); + let line_no = format!("{line}: "); + + eprintln!("{line_no}{}", lines[line - 1]); + + for (i, err_line) in err_msg.to_string().lines().enumerate() { + // Display position marker + println!( + "{0:>1$}{err_line}", + if i > 0 { "| " } else { "^ " }, + line_no.len() + pos.position().unwrap() + 1, + ); + } + eprintln!(); + } + + let lines: Vec<_> = input.lines().collect(); + + // Print error + let pos = err.take_position(); + + if pos.is_none() { + // No position + eprintln!("{err}"); + } else { + // Specific position + eprint_line(&lines, pos, &err.to_string()); + } +} + +#[derive(Parser)] +#[command( + author, + version, + name = "rusp-run", + about = "the Rust USP toolkit, rhai runner" +)] +struct Rusp { + #[arg(long = "script", short = 's')] + /// Inline rhai script + script: Option, + /// Output filename of file to encode USP Protobuf message to + filename: Option, +} + +fn main() { + let args = Rusp::parse(); + + // Initialize scripting engine + let mut engine = Engine::new(); + + // Create rusp package and add the package into the engine + engine.register_static_module("rusp", RuspPackage::new().as_shared_module()); + engine.set_optimization_level(rhai::OptimizationLevel::Simple); + + if let Some(filename) = args.filename { + let mut contents = String::new(); + let filename = match Path::new(&filename).canonicalize() { + Err(err) => { + eprintln!("Error script file path: {filename:?}\n{err}"); + exit(1); + } + Ok(f) => match f.strip_prefix(std::env::current_dir().unwrap().canonicalize().unwrap()) + { + Ok(f) => f.into(), + _ => f, + }, + }; + + let mut f = match File::open(&filename) { + Err(err) => { + eprintln!( + "Error reading script file: {}\n{}", + filename.to_string_lossy(), + err + ); + exit(1); + } + Ok(f) => f, + }; + + if let Err(err) = f.read_to_string(&mut contents) { + eprintln!( + "Error reading script file: {}\n{}", + filename.to_string_lossy(), + err + ); + exit(1); + } + + let contents = if contents.starts_with("#!") { + // Skip shebang + &contents[contents.find('\n').unwrap_or(0)..] + } else { + &contents[..] + }; + + if let Err(err) = engine + .compile(contents) + .map_err(Into::into) + .and_then(|mut ast| { + ast.set_source(filename.to_string_lossy().to_string()); + engine.run_ast(&ast) + }) + { + let filename = filename.to_string_lossy(); + + eprintln!("{:=<1$}", "", filename.len()); + eprintln!("{filename}"); + eprintln!("{:=<1$}", "", filename.len()); + eprintln!(); + + eprint_error(contents, *err); + } + } else if let Some(contents) = args.script { + let filename = "