ruwren 0.4.10

Rusty bindings to Wren programming language
Documentation
# Ruwren: Wren bindings for Rust [![Crates.io]https://img.shields.io/crates/v/ruwren]https://crates.io/crates/ruwren [![docs.rs]https://docs.rs/ruwren/badge.svg]https://docs.rs/ruwren/

Build status: [![Travis CI](https://travis-ci.com/Jengamon/ruwren.svg?branch=master)](https://travis-ci.com/github/Jengamon/ruwren)

Here is an attempt at making some Rust Wren bindings in a more Rust style.
It acts at times pretty transparently (you do have to deal with the slot / foreign API), but should be

- More typesafe than using the C API directly
- Shouldn't get in the way of quick execution
- Should be relatively simple to get started

## Including

Just add:

```toml
ruwren = "0.4"
```

to your Cargo.toml.

## Main API

### Creating a VM

To create a VM, use the VMConfig struct. To create a basic VM that logs it's output to the console, simply call

```rust
let vm = VMConfig::new().build();
```

You can run code by using interpret directly:

```rust
vm.interpret("main", r##"System.print("Cool beans!")"##).unwrap();
```

Which returns a `Ok(())` on successful execution, and `Err(e)` on failure (see VMError for more details).

You can also call code defined in Wren using a FunctionHandle like so:

```rust
vm.interpret("main", r##"
class GameEngine {
    static update(delta) {
        System.print(delta)
    }
}
"##).unwrap();
let handle = vm.make_call_handle(FunctionSignature::new_function("update", 1));
vm.execute(|vm| {
    vm.ensure_slots(2);
    vm.get_variable("main", "GameEngine", 0);
    vm.set_slot_double(1, 0.016);
});
vm.call_handle(&handle);
```

or more directly:

```rust
vm.interpret("main", r##"
class GameEngine {
    static update(delta) {
        System.print(delta)
    }
}
"##).unwrap();
vm.execute(|vm| {
    vm.ensure_slots(2);
    vm.get_variable("main", "GameEngine", 0);
    vm.set_slot_double(1, 0.016);
});
vm.call(FunctionSignature::new_function("update", 1));
```

## Embedding Rust code in Wren

Here's a short example of how you can embed your Rust data into Wren:

```rust
use ruwren::{Class, VM, VMConfig, ModuleLibrary, get_slot_checked, create_module};
struct Foo {
    bar: f64,
}

impl Class for Foo {
    fn initialize(vm: &VM) -> Self {
        let bar = get_slot_checked!(vm => num 1);
        Foo { bar }
    }
}

impl Foo {
    fn instance(&self, vm: &VM) {
        vm.set_slot_double(0, self.bar);
    }

    fn static_fn(vm: &VM) {
        let num = get_slot_checked!(vm => num 1);
        vm.set_slot_double(0, num + 5.0)
    }
}

create_module! {
    class("Foo") crate::Foo => foo {
        instance(fn "instance", 0) instance,
        static(fn "static_fn", 1) static_fn
    }

    module => foobar
}

fn main() {
    let mut lib = ModuleLibrary::new();
    foobar::publish_module(&mut lib);

    let vm = VMConfig::new().library(&lib).build();
    vm.interpret("foobar", r##"
    foreign class Foo {
        construct new(bar) {}
        foreign instance()
        foreign static static_fn(num)
    }
    "##).unwrap();

    // You could now write Wren code like:

    vm.interpret("main", r##"
    import "foobar" for Foo
    var f = Foo.new(4)
    System.print(Foo.static_fn(f.instance()))
    "##).unwrap();

    // This should print "9".
}
```

### V2 Foreign

V2 foreigns emulate Wren's class system on top of the original
foreign API, so the above example would be:
```rust
use ruwren::{wren_impl, wren_module, ModuleLibrary, VMConfig, WrenObject};
#[derive(WrenObject, Default)]
struct Foo {
    bar: f64,
}

#[wren_impl]
impl Foo {
    /*
    you can also write out an allocator, if you
    don't want the base struct itself to implement Default

    #[wren_impl(allocator)]
    fn allocator() -> FooClass {
        FooClass {}
    }
    */

    #[wren_impl(constructor)]
    fn constructor(&self, bar: f64) -> FooInstance {
        FooInstance { bar }
    }

    #[wren_impl(instance)]
    fn instance(&self) -> f64 {
        self.bar
    }

    fn static_fn(&self, num: f64) -> f64 {
        num + 5.0
    }
}

wren_module! {
    mod foobar {
        pub crate::Foo;
    }
}

fn main() {
    let mut lib = ModuleLibrary::new();
    foobar::publish_module(&mut lib);

    let vm = VMConfig::new().library(&lib).build();
    vm.interpret("foobar", r##"
    foreign class Foo {
        construct new(bar) {}
        foreign instance()
        foreign static static_fn(num)
    }
    "##).unwrap();

    // You could now write Wren code like:

    vm.interpret("main", r##"
    import "foobar" for Foo
    var f = Foo.new(4)
    System.print(Foo.static_fn(f.instance()))
    "##).unwrap();

    // This should print "9".
}
```

## About WASM Compilation

It technically works as long as you target WASI, and have a WASI SDK setup somewhere.
look at the justfile or example-wasi.nu for the environment variables to set to get it running. There is one big caveat tho:

WASM (even with WASI) is a panic=abort platform, so catch_unwind does nothing,
and panics are unhandleable.

This means that some idioms of the v1 foreign API (namely `get_slot_checked!`) are not very good on the platform. Basically, anything that panics doesn't work well.

With a minimal change the the v2 foreign API (namely, having the constructor be fallible) means that v2 should work relatively unchanged on web, and v1 is *usable*, it just shouldn't trigger a panic, or the wasm runtime will flip the table.