From 4003037ca87f7aa4127ccbfa3888488203507c2e Mon Sep 17 00:00:00 2001 From: Isaac Clayton Date: Wed, 1 Jun 2022 16:39:59 +0200 Subject: [PATCH] Documented code, got basic example working --- Cargo.lock | 7 +++ crates/runner/Cargo.toml | 1 + crates/runner/plugin/cargo_test.lua | 22 ++++++++++ crates/runner/src/lib.rs | 40 +++-------------- crates/runner/src/lua.rs | 62 ++++++++++++++++++++++++++ crates/runner/src/main.rs | 57 +++++++++++++++++++----- crates/runner/src/runtime.rs | 68 ++++++++++++++++++++++++----- 7 files changed, 200 insertions(+), 57 deletions(-) create mode 100644 crates/runner/plugin/cargo_test.lua create mode 100644 crates/runner/src/lua.rs diff --git a/Cargo.lock b/Cargo.lock index ec2078d493..64fb6d2808 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2654,6 +2654,12 @@ dependencies = [ "libc", ] +[[package]] +name = "map-macro" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b0858fc6e216d2d6222d661021d9b184550acd757fbd80a8f86224069422c" + [[package]] name = "matchers" version = "0.1.0" @@ -4011,6 +4017,7 @@ dependencies = [ name = "runner" version = "0.1.0" dependencies = [ + "map-macro", "mlua", "serde", ] diff --git a/crates/runner/Cargo.toml b/crates/runner/Cargo.toml index eae03bca0c..85cebd26f9 100644 --- a/crates/runner/Cargo.toml +++ b/crates/runner/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" [dependencies] mlua = { version = "0.8.0-beta.5", features = ["lua54", "vendored", "serialize"] } serde = "1.0" +map-macro = "0.2" # bincode = "1.3" diff --git a/crates/runner/plugin/cargo_test.lua b/crates/runner/plugin/cargo_test.lua new file mode 100644 index 0000000000..3921a1b07e --- /dev/null +++ b/crates/runner/plugin/cargo_test.lua @@ -0,0 +1,22 @@ +print("initializing plugin...") + +query = [[( + (attribute_item + (meta_item + (identifier) @test)) @attribute + . + (function_item + name: (identifier) @name) @funciton +)]] + +function run_test(name) + print('running test `' .. name .. '`:') + local command = 'cargo test -- ' .. name + local openPop = assert(io.popen(command, 'r')) + local output = openPop:read('*all') + openPop:close() + print('done running test') + return output +end + +print("done initializing plugin.") \ No newline at end of file diff --git a/crates/runner/src/lib.rs b/crates/runner/src/lib.rs index 82aaa62368..4afe88b85a 100644 --- a/crates/runner/src/lib.rs +++ b/crates/runner/src/lib.rs @@ -1,40 +1,10 @@ -use std::collections::{HashMap, HashSet}; +use mlua::{Function, Lua, LuaSerdeExt, Value}; +use serde::{de::DeserializeOwned, Serialize}; -use mlua::{Error, FromLua, Function, Lua, LuaSerdeExt, ToLua, UserData, Value}; +pub use map_macro::{map, set}; pub mod runtime; pub use runtime::*; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -impl Runtime for Lua { - type Module = String; - - fn init(module: Self::Module) -> Option { - let lua = Lua::new(); - lua.load(&module).exec().ok()?; - return Some(lua); - } - - fn handles(&self) -> Handles { - let mut globals = HashSet::new(); - for pair in self.globals().pairs::() { - if let Ok((k, _)) = pair { - globals.insert(k); - } - } - - globals - } - - fn val(&self, name: String) -> Option { - let val: Value = self.globals().get(name).ok()?; - Some(self.from_value(val).ok()?) - } - - fn call(&self, name: String, arg: T) -> Option { - let fun: Function = self.globals().get(name).ok()?; - let arg: Value = self.to_value(&arg).ok()?; - let result = fun.call(arg).ok()?; - Some(self.from_value(result).ok()?) - } -} +pub mod lua; +pub use lua::*; diff --git a/crates/runner/src/lua.rs b/crates/runner/src/lua.rs new file mode 100644 index 0000000000..4e364b9556 --- /dev/null +++ b/crates/runner/src/lua.rs @@ -0,0 +1,62 @@ +use mlua::Result; + +use crate::*; + +impl Runtime for Lua { + type Plugin = LuaPlugin; + type Error = mlua::Error; + + fn init(module: Self::Plugin) -> Result { + let lua = Lua::new(); + + // for action in module.actions { + // action(&mut lua).ok()?; + // } + + lua.load(&module.source).exec()?; + return Ok(lua); + } + + fn constant(&mut self, handle: &Handle) -> Result { + let val: Value = self.globals().get(handle.inner())?; + Ok(self.from_value(val)?) + } + + fn call(&mut self, handle: &Handle, arg: T) -> Result { + let fun: Function = self.globals().get(handle.inner())?; + let arg: Value = self.to_value(&arg)?; + let result = fun.call(arg)?; + Ok(self.from_value(result)?) + } + + fn register_handle>(&mut self, name: T) -> bool { + self.globals() + .contains_key(name.as_ref().to_string()) + .unwrap_or(false) + } +} + +pub struct LuaPlugin { + // name: String, + source: String, + // actions: Vec Result<(), ()>>>, +} + +impl LuaPlugin { + pub fn new( + // name: String, + source: String, + ) -> LuaPlugin { + LuaPlugin { + // name, + source, + // actions: Vec::new(), + } + } + + // pub fn setup(mut self, action: fn(&mut Lua) -> Result<(), ()>) -> LuaPlugin { + // let action = Box::new(action); + // self.actions.push(action); + // self + // } +} diff --git a/crates/runner/src/main.rs b/crates/runner/src/main.rs index 48cdfb07b6..39e8276696 100644 --- a/crates/runner/src/main.rs +++ b/crates/runner/src/main.rs @@ -1,25 +1,58 @@ -use mlua::{Lua, Result}; +use mlua::Lua; use runner::*; -pub fn main() { - let lua: Lua = Runtime::init( - "query = \"Some random tree-sitter query\"\nprint(\"Hello from the Lua test runner!\")" - .to_string(), - ) - .unwrap(); +pub fn main() -> Result<(), mlua::Error> { + let source = include_str!("../plugin/cargo_test.lua").to_string(); + + let module = LuaPlugin::new(source); + // .setup(|runtime| { + // let greet = runtime + // .create_function(|_, name: String| { + // println!("Hello, {}!", name); + // Ok(()) + // }) + // .map_err(|_| ())?; + + // runtime.globals().set("greet", greet).map_err(|_| ())?; + // Ok(()) + // }); + + let mut lua: Lua = Runtime::init(module)?; let runner: TestRunner = lua.as_interface::().unwrap(); - println!("{:#?}", runner); + + println!("extracted interface: {:#?}", &runner); + + let contents = runner.run_test(&mut lua, "it_works".into()); + + println!("test results:{}", contents.unwrap()); + + Ok(()) } +#[allow(dead_code)] #[derive(Debug)] struct TestRunner { - query: String, + pub query: String, + run_test: Handle, } impl Interface for TestRunner { - fn from_runtime(runtime: &T) -> Option { - let query: String = runtime.val("query".to_string())?; - Some(TestRunner { query }) + fn from_runtime(runtime: &mut T) -> Option { + let run_test = runtime.handle_for("run_test")?; + let query = runtime.handle_for("query")?; + let query: String = runtime.constant(&query).ok()?; + Some(TestRunner { query, run_test }) } } + +impl TestRunner { + pub fn run_test(&self, runtime: &mut T, test_name: String) -> Option { + runtime.call(&self.run_test, test_name).ok() + } +} + +#[test] +pub fn it_works() { + panic!("huh, that was surprising..."); +} diff --git a/crates/runner/src/runtime.rs b/crates/runner/src/runtime.rs index da4fd0de30..3ee03e961c 100644 --- a/crates/runner/src/runtime.rs +++ b/crates/runner/src/runtime.rs @@ -1,29 +1,77 @@ -use std::collections::HashSet; +// use std::Error; -use mlua::{FromLua, Lua, ToLua, Value}; use serde::{de::DeserializeOwned, Serialize}; -pub type Handles = HashSet; +/// Represents a handle to a constant or function in the Runtime. +/// Should be constructed by calling [`Runtime::handle_for`]. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct Handle(String); +impl Handle { + pub fn inner(&self) -> &str { + &self.0 + } +} + +/// Represents an interface that can be implemented by a plugin. pub trait Interface where Self: Sized, { - fn from_runtime(runtime: &T) -> Option; + /// Create an interface from a given runtime. + /// All handles to be used by the interface should be registered and stored in `Self`. + fn from_runtime(runtime: &mut T) -> Option; } pub trait Runtime where Self: Sized, { - type Module; + /// Represents a plugin to be loaded by the runtime, + /// e.g. some source code + anything else needed to set up. + type Plugin; - fn init(plugin: Self::Module) -> Option; - fn handles(&self) -> Handles; - fn val(&self, name: String) -> Option; - fn call(&self, name: String, arg: T) -> Option; + /// The error type for this module. + /// Ideally should implement the [`std::err::Error`] trait. + type Error; - fn as_interface(&self) -> Option { + /// Initializes a plugin, returning a [`Runtime`] that can be queried. + /// Note that if you have any configuration, + fn init(plugin: Self::Plugin) -> Result; + + /// Returns a top-level constant from the module. + /// This can be used to extract configuration information from the module, for example. + /// Before calling this function, get a handle into the runtime using [`handle_for`]. + fn constant(&mut self, handle: &Handle) -> Result; + + /// Call a function defined in the module. + fn call( + &mut self, + handle: &Handle, + arg: T, + ) -> Result; + + /// Registers a handle with the runtime. + /// This is a mutable item if needed, but generally + /// this should be an immutable operation. + /// Returns whether the handle exists/was successfully registered. + fn register_handle>(&mut self, name: T) -> bool; + + /// Returns the handle for a given name if the handle is defined. + /// Will only return an error if there was an error while trying to register the handle. + /// This function uses [`register_handle`], no need to implement this one. + fn handle_for>(&mut self, name: T) -> Option { + if self.register_handle(&name) { + Some(Handle(name.as_ref().to_string())) + } else { + None + } + } + + /// Creates the given interface from the current module. + /// Returns [`Error`] if the provided plugin does not match the expected interface. + /// Essentially wraps the [`Interface`] trait. + fn as_interface(&mut self) -> Option { Interface::from_runtime(self) } }