prelude!();
use std::sync::Arc;
use anyhow::{anyhow, bail, Context as _, Result};
use rune::compile::Named;
use rune::runtime::{MaybeTypeOf, ToValue, Type, TypeOf};
use rune::termcolor;
use rune::{Any, Context, ContextError, Diagnostics, Module, Sources, Vm};
#[derive(Any)]
#[rune(item = ::native_crate)]
struct Generic<T>
where
T: 'static + TryClone + Named + FromValue + ToValue + MaybeTypeOf + TypeOf,
{
#[rune(get, set)]
data: T,
}
impl<T> Generic<T>
where
T: 'static + TryClone + Copy + Named + FromValue + ToValue + MaybeTypeOf + TypeOf,
{
fn get_value(&self) -> T {
self.data
}
}
fn make_native_module() -> Result<Module, ContextError> {
let mut module = Module::with_crate("native_crate")?;
module.ty::<Generic<i64>>()?;
module.associated_function("get_value", Generic::<i64>::get_value)?;
module.ty::<Generic<f64>>()?;
module.associated_function("get_value", Generic::<f64>::get_value)?;
Ok(module)
}
fn compile(mut sources: Sources) -> Result<Vm> {
let mut context = Context::with_default_modules()?;
context.install(make_native_module()?)?;
let mut diagnostics = Diagnostics::default();
let result = rune::prepare(&mut sources)
.with_context(&context)
.with_diagnostics(&mut diagnostics)
.build();
if !diagnostics.is_empty() {
let mut out = termcolor::Buffer::no_color();
diagnostics.emit(&mut out, &sources)?;
let out = String::from_utf8(out.into_inner())?;
bail!("{out}")
}
let unit = result?;
Ok(Vm::new(Arc::new(context.runtime()?), Arc::new(unit)))
}
#[test]
fn test_generic() -> Result<()> {
macro_rules! test {
($($ty:ty, $function:ident, $function_ty:ident, $value:expr, $set:expr),+ $(,)?) => {
let mut vm = compile(rune::sources! {
entry => {
pub fn get_value(v) { v.get_value() }
pub fn get_data(v) { v.data }
pub fn set_data(v, value) { v.data = value; }
$(pub fn $function(v) { ::native_crate::Generic::<$ty>::get_value(v) })*
$(pub fn $function_ty() { ::native_crate::Generic::<$ty> })*
}
})?;
$(
let t1: Generic<$ty> = Generic { data: $value };
let value = vm.call(["get_value"], (t1,)).with_context(|| anyhow!("{}: get_value: Working call", stringify!($ty)))?;
let value: $ty = rune::from_value(value).with_context(|| anyhow!("{}: get_value: Output value", stringify!($ty)))?;
assert_eq!(value, $value);
)*
$(
let t1: Generic<$ty> = Generic { data: $value };
let value = vm.call(["get_data"], (t1,)).with_context(|| anyhow!("{}: get_data: Working call", stringify!($ty)))?;
let value: $ty = rune::from_value(value).with_context(|| anyhow!("{}: get_data: Output value", stringify!($ty)))?;
assert_eq!(value, $value);
)*
$(
let mut t1: Generic<$ty> = Generic { data: $value };
let _ = vm.call(["set_data"], (&mut t1, $set)).with_context(|| anyhow!("{}: set_data: Working call", stringify!($ty)))?;
assert_eq!(t1.data, $set);
)*
$(
let t1: Generic<$ty> = Generic { data: $value };
let value = vm.call([stringify!($function)], (t1,)).with_context(|| anyhow!("{}: {}: Working call", stringify!($ty), stringify!($function)))?;
let value: $ty = rune::from_value(value).with_context(|| anyhow!("{}: {}: Output value", stringify!($ty), stringify!($function)))?;
assert_eq!(value, $value);
)*
$(
let value = vm.call([stringify!($function_ty)], ()).with_context(|| anyhow!("{}: {}: Working call", stringify!($ty), stringify!($function_ty)))?;
let value: Type = rune::from_value(value).with_context(|| anyhow!("{}: {}: Output value", stringify!($ty), stringify!($function_ty)))?;
assert_eq!(Generic::<$ty>::HASH, value.into_hash());
)*
};
}
test! {
i64, test_int, test_int_ty, 3, 30,
f64, test_float, test_float_ty, 2.0, 20.0,
};
Ok(())
}