elm-rust-binding 0.4.0

Call Elm functions from Rust in an ergonomic way
# Elm Rust Binding

This crate offers a way to conveniently call an Elm function from Rust.
The main motivation here is for testing purposes:

- You have some logic in your Elm frontend that cannot be moved to the backend (because e.g. it is called in a hot loop)
- You have related logic in your Rust backend

Then you can call your Elm code in a Rust test with this crate to verify the two implementations are in sync.
The performance this crate offers is NOT optimal - do not use this for Interop in production code.

## Example

### Rust side

```rust
use elm_rust_binding::{ElmRoot, Result};

#[test]
fn call_elm() -> Result<()> {
    let mut elm_root = ElmRoot::new("../frontend/src")?;
    let mut square_fn = elm_root.prepare("Math.square")?;
    let squared = square_fn.call(4)?;
    assert_eq!(squared, 16);
    Ok(())
}
```

### Elm side

```elm
-- In /frontend/src/Math.elm
module Math exposing (square)


square : Int -> Int
square n = n * n
```

Note that you can call the function multiple times, which will improve performance over creating a new `ElmRoot` and `ElmFunctionHandle`.
This is especially useful for fuzz/property-based testing

## Implementation Details

How does this work under the hood? It essentially boils down to 6 steps:

1. Infer the Elm input and output types based on the Rust type annotations (i32 -> Int, bool -> Bool, etc.).
2. Generate an .elm file with a `Platform.worker` main function. It will accept the input type over its flags and return the output type via a port. Internally it will call the specified function by importing it.
3. Invoke the Elm compiler on the generated file, producing a .js file.
4. Make the produced .js file ESM-compatible.
5. Load the module into a Deno runtime.
6. Initialize the Elm application every time `.call` is invoked by passing the input argument as flags and getting the output via `.subscribe`.

The generated files are removed automatically and are postfixed with a UUID to prevent different invocations messing with each other.
If you call `.debug()` on the `ElmRoot`, the files are not removed to help debugging issues.