├── native ├── rhai_rustler │ ├── .gitignore │ ├── Cross.toml │ ├── src │ │ ├── lib.rs │ │ ├── ast.rs │ │ ├── types.rs │ │ ├── scope.rs │ │ ├── error.rs │ │ └── engine.rs │ ├── .cargo │ │ └── config │ ├── Cargo.toml │ └── Cargo.lock └── test_dylib_module │ ├── .gitignore │ ├── .cargo │ └── config.toml │ ├── Cargo.toml │ ├── src │ ├── lib.rs │ └── api.rs │ └── Cargo.lock ├── test ├── test_helper.exs ├── fixtures │ ├── script.rhai │ └── script_with_scope.rhai ├── support │ └── test_dylib_module.ex └── rhai │ ├── property_test.exs │ ├── ast_test.exs │ ├── scope_test.exs │ └── engine_test.exs ├── .formatter.exs ├── lib └── rhai │ ├── any.ex │ ├── package.ex │ ├── error.ex │ ├── ast.ex │ ├── native.ex │ └── scope.ex ├── .dialyzer_ignore.exs ├── .github ├── dependabot.yml └── workflows │ ├── rust-ci.yaml │ ├── main.yaml │ └── release.yaml ├── .gitignore ├── mix.exs ├── README.md ├── mix.lock ├── .credo.exs ├── LICENSE └── CHANGELOG.md /native/rhai_rustler/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /native/test_dylib_module/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /test/fixtures/script.rhai: -------------------------------------------------------------------------------- 1 | fn test(x) { 2 | x + 1 3 | } 4 | 5 | test(42) 6 | -------------------------------------------------------------------------------- /native/rhai_rustler/Cross.toml: -------------------------------------------------------------------------------- 1 | [build.env] 2 | passthrough = ["RUSTLER_NIF_VERSION"] 3 | -------------------------------------------------------------------------------- /test/fixtures/script_with_scope.rhai: -------------------------------------------------------------------------------- 1 | fn test(x) { 2 | a + b + x 3 | } 4 | 5 | let x = test(42); 6 | 7 | x 8 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"], 4 | import_deps: [:stream_data] 5 | ] 6 | -------------------------------------------------------------------------------- /lib/rhai/any.ex: -------------------------------------------------------------------------------- 1 | defmodule Rhai.Any do 2 | @moduledoc """ 3 | Rhai types 4 | """ 5 | 6 | @type t() :: 7 | number() | boolean() | String.t() | nil | [t()] | %{String.t() => t()} 8 | end 9 | -------------------------------------------------------------------------------- /native/test_dylib_module/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_os = "macos")'] 2 | rustflags = [ 3 | "-C", "link-arg=-undefined", 4 | "-C", "link-arg=dynamic_lookup", 5 | ] 6 | 7 | [profile.release] 8 | lto = true 9 | -------------------------------------------------------------------------------- /.dialyzer_ignore.exs: -------------------------------------------------------------------------------- 1 | [ 2 | # something is wrong with the RustlerPrecompiled use macro 3 | # this fixes it by ignoring the Rhai.Native module completely 4 | {"lib/rhai/native.ex"}, 5 | {"test/support/test_dylib_module.ex"} 6 | ] 7 | -------------------------------------------------------------------------------- /native/test_dylib_module/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_dylib_module" 3 | version = "0.1.0" 4 | authors = [] 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "test_dylib_module" 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | rhai-dylib = { version = "=0.6.0", features = ["sync"] } 14 | rustler = "0.36.1" 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "mix" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | - package-ecosystem: "cargo" 12 | directory: "/native/rhai_rustler" 13 | schedule: 14 | interval: "daily" 15 | - package-ecosystem: "cargo" 16 | directory: "/native/test_dylib_module" 17 | schedule: 18 | interval: "daily" 19 | -------------------------------------------------------------------------------- /lib/rhai/package.ex: -------------------------------------------------------------------------------- 1 | defmodule Rhai.Package do 2 | @moduledoc """ 3 | Rhai package types 4 | """ 5 | 6 | @type t :: 7 | :arithmetic 8 | | :basic_array 9 | | :basic_blob 10 | | :basic_fn 11 | | :basic_iterator 12 | | :basic_math 13 | | :basic_string 14 | | :basic_time 15 | | :bit_field 16 | | :core 17 | | :language_core 18 | | :logic 19 | | :more_string 20 | | :standard 21 | end 22 | -------------------------------------------------------------------------------- /native/rhai_rustler/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod ast; 2 | mod engine; 3 | mod error; 4 | mod scope; 5 | mod types; 6 | 7 | use rhai::config::hashing::set_hashing_seed; 8 | use rustler::{Env, Term}; 9 | 10 | fn load(_: Env, _: Term) -> bool { 11 | // Set dylib ahash seed 12 | if let Err(value) = set_hashing_seed(Some([1, 3, 3, 7])) { 13 | eprintln!( 14 | "Failed to set ahash seed, ahash seed already set: {:?}", 15 | value 16 | ); 17 | return false; 18 | } 19 | 20 | true 21 | } 22 | 23 | rustler::init!("Elixir.Rhai.Native", load = load); 24 | -------------------------------------------------------------------------------- /native/test_dylib_module/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod api; 2 | 3 | use rhai_dylib::rhai::{config::hashing::set_hashing_seed, exported_module, Module, Shared}; 4 | 5 | #[allow(improper_ctypes_definitions)] 6 | #[no_mangle] 7 | pub extern "C" fn module_entrypoint() -> Shared { 8 | if let Err(value) = set_hashing_seed(Some([1, 3, 3, 7])) { 9 | if value != Some([1, 3, 3, 7]) { 10 | panic!("ahash seed was already set with value: {value:?}"); 11 | } 12 | } 13 | 14 | exported_module!(api::my_plugin_api).into() 15 | } 16 | 17 | rustler::init!("Elixir.Rhai.TestDylibModule"); 18 | -------------------------------------------------------------------------------- /native/rhai_rustler/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.'cfg(target_os = "macos")'] 2 | rustflags = [ 3 | "-C", "link-arg=-undefined", 4 | "-C", "link-arg=dynamic_lookup", 5 | ] 6 | 7 | # See https://github.com/rust-lang/rust/issues/59302 8 | [target.x86_64-unknown-linux-musl] 9 | rustflags = [ 10 | "-C", "target-feature=-crt-static" 11 | ] 12 | 13 | # See https://github.com/rust-lang/cargo/issues/7154 14 | [target.aarch64-unknown-linux-musl] 15 | rustflags = [ 16 | "-C", "target-feature=-crt-static" 17 | ] 18 | 19 | # Provides a small build size, but takes more time to build. 20 | [profile.release] 21 | lto = true 22 | -------------------------------------------------------------------------------- /native/rhai_rustler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rhai_rustler" 3 | version = "1.2.0" 4 | authors = ["Fabrizio Sestito "] 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "rhai_rustler" 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | rhai = { version = "=1.21.0", features = ["sync"] } 14 | rhai-dylib = { version = "0.5.0", features = ["sync"] } 15 | thiserror = "2.0.12" 16 | rustler = "0.36.1" 17 | 18 | [features] 19 | nif_version_2_15 = ["rustler/nif_version_2_15"] 20 | nif_version_2_16 = ["rustler/nif_version_2_16"] 21 | nif_version_2_17 = ["rustler/nif_version_2_17"] 22 | -------------------------------------------------------------------------------- /test/support/test_dylib_module.ex: -------------------------------------------------------------------------------- 1 | defmodule Rhai.TestDylibModule do 2 | @moduledoc """ 3 | This module is used to test the integration with rhai-dylib. 4 | 5 | Rustler is needed to trigger the compilation of the crate, and it does not export NIFs. 6 | """ 7 | 8 | use Rustler, 9 | otp_app: :rhai_rustler, 10 | crate: :test_dylib_module 11 | 12 | if :os.type() == {:unix, :darwin} do 13 | @after_compile __MODULE__ 14 | 15 | # This is a workaround since Rustler creates a .so file on macOS, 16 | # but the dylib module resolver expects a .dylib file. 17 | def __after_compile__(_, _) do 18 | path = File.cwd!() <> "/priv/native/libtest_dylib_module" 19 | 20 | File.ln_s(path <> ".so", path <> ".dylib") 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | rhai_rustler-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | 28 | # Rustler 29 | /priv/native/* 30 | 31 | checksum-Elixir.Rhai.Native.exs 32 | 33 | .elixir-ls 34 | -------------------------------------------------------------------------------- /.github/workflows/rust-ci.yaml: -------------------------------------------------------------------------------- 1 | name: Rust CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths: 7 | - "native/**" 8 | pull_request: 9 | paths: 10 | - "native/**" 11 | workflow_dispatch: 12 | 13 | jobs: 14 | lint_rust: 15 | name: Lint Rust 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | manifest: 20 | - native/rhai_rustler/Cargo.toml 21 | 22 | steps: 23 | - name: Check out this repository 24 | uses: actions/checkout@v4 25 | 26 | - name: Install Rust 27 | uses: actions-rs/toolchain@v1 28 | with: 29 | profile: minimal 30 | toolchain: stable 31 | components: rustfmt, clippy 32 | override: true 33 | 34 | - name: Check if code is formatted 35 | uses: actions-rs/cargo@v1 36 | with: 37 | command: fmt 38 | args: --manifest-path=${{ matrix.manifest }} --all -- --check 39 | 40 | - name: Run clippy 41 | uses: actions-rs/cargo@v1 42 | with: 43 | command: clippy 44 | args: --manifest-path=${{ matrix.manifest }} -- -Dwarnings 45 | -------------------------------------------------------------------------------- /lib/rhai/error.ex: -------------------------------------------------------------------------------- 1 | defmodule Rhai.Error do 2 | @moduledoc """ 3 | Rhai error types 4 | """ 5 | 6 | @type error :: 7 | :system 8 | | :parsing 9 | | :variable_exists 10 | | :forbidden_variable 11 | | :variable_not_found 12 | | :property_not_found 13 | | :index_not_found 14 | | :function_not_found 15 | | :module_not_found 16 | | :in_function_call 17 | | :in_module 18 | | :unbound_this 19 | | :mismatch_data_type 20 | | :mismatch_output_type 21 | | :indexing_type 22 | | :array_bounds 23 | | :string_bounds 24 | | :bit_field_bounds 25 | | :for_atom 26 | | :data_race 27 | | :assignment_to_constant 28 | | :dot_expr 29 | | :arithmetic 30 | | :too_many_operations 31 | | :too_many_modules 32 | | :stack_overflow 33 | | :data_too_large 34 | | :terminated 35 | | :custom_syntax 36 | | :runtime 37 | | :non_pure_method_call_on_constant 38 | | :scope_is_empty 39 | | :cannot_update_value_of_constant 40 | | :custom_operator 41 | 42 | @type t() :: {error(), String.t()} 43 | end 44 | -------------------------------------------------------------------------------- /native/test_dylib_module/src/api.rs: -------------------------------------------------------------------------------- 1 | use rhai_dylib::rhai::plugin::*; 2 | use rhai_dylib::rhai::{Map, Module, INT}; 3 | 4 | #[derive(Clone)] 5 | pub struct MyPluginObjectInner { 6 | inner: String, 7 | } 8 | 9 | // The plugin API from rhai can be used to create your plugin API. 10 | #[rhai_dylib::rhai::export_module] 11 | pub mod my_plugin_api { 12 | 13 | // Implement a custom type. 14 | type MyPluginObject = MyPluginObjectInner; 15 | 16 | // Constructor for the custom type. 17 | #[rhai_fn(global)] 18 | pub fn new_plugin_object(inner: &str) -> MyPluginObject { 19 | MyPluginObject { 20 | inner: inner.to_string(), 21 | } 22 | } 23 | 24 | /// A function for the custom type. 25 | #[rhai_fn(global)] 26 | pub fn get_inner(s: MyPluginObject) -> String { 27 | s.inner 28 | } 29 | 30 | /// Computing something and returning a result. 31 | #[rhai_fn(global)] 32 | pub fn triple_add(a: INT, b: INT, c: INT) -> INT { 33 | a + b + c 34 | } 35 | 36 | /// Custom operator 37 | #[rhai_fn(name = "#", global)] 38 | pub fn custom_operator(a: INT, b: INT) -> INT { 39 | a + b 40 | } 41 | 42 | /// Using Rhai types, non-global function. 43 | #[rhai_fn()] 44 | pub fn get_property(m: &mut Map) -> String { 45 | m.get("property").unwrap().clone_cast() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Rhai.MixProject do 2 | use Mix.Project 3 | 4 | @version "1.2.0" 5 | 6 | def project do 7 | [ 8 | app: :rhai_rustler, 9 | version: @version, 10 | elixir: "~> 1.11", 11 | elixirc_paths: elixirc_paths(Mix.env()), 12 | start_permanent: Mix.env() == :prod, 13 | description: description(), 14 | package: package(), 15 | docs: docs(), 16 | deps: deps() 17 | ] 18 | end 19 | 20 | def application do 21 | [ 22 | extra_applications: [:logger] 23 | ] 24 | end 25 | 26 | defp description do 27 | "Rhai rustler bindings" 28 | end 29 | 30 | defp package do 31 | [ 32 | files: [ 33 | "lib", 34 | "native/rhai_rustler/.cargo", 35 | "native/rhai_rustler/src", 36 | "native/rhai_rustler/Cargo*", 37 | "checksum-*.exs", 38 | "mix.exs", 39 | "README.md", 40 | "guides/nif-bindings.md", 41 | "LICENSE" 42 | ], 43 | licenses: ["Apache-2.0"], 44 | mantainers: ["Fabrizio Sestito "], 45 | links: %{ 46 | "GitHub" => "https://github.com/rhaiscript/rhai_rustler", 47 | "Docs" => "https://hexdocs.pm/rhai_rustler/" 48 | } 49 | ] 50 | end 51 | 52 | defp docs do 53 | [ 54 | main: "readme", 55 | extras: ["README.md", "guides/nif-bindings.md", "LICENSE"] 56 | ] 57 | end 58 | 59 | # Specifies which paths to compile per environment. 60 | defp elixirc_paths(:test), 61 | do: [ 62 | "lib", 63 | "test/support" 64 | ] 65 | 66 | defp elixirc_paths(_), do: ["lib"] 67 | 68 | defp deps do 69 | [ 70 | {:rustler, "~> 0.36.0"}, 71 | {:rustler_precompiled, "~> 0.8.0"}, 72 | {:ex_doc, ">= 0.0.0", only: :dev, runtime: false}, 73 | {:credo, "~> 1.6", only: [:dev, :test], runtime: false}, 74 | {:stream_data, "~> 1.0", only: [:dev, :test]}, 75 | {:dialyxir, "~> 1.3", only: [:dev, :test], runtime: false} 76 | ] 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | env: 10 | RHAI_RUSTLER_FORCE_BUILD: "true" 11 | 12 | jobs: 13 | test: 14 | name: Test (Elixir ${{matrix.elixir}} | Erlang/OTP ${{matrix.otp}}) 15 | runs-on: ubuntu-20.04 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | include: 20 | - otp: 27 21 | elixir: 1.17.3 22 | lint: true 23 | - otp: 26 24 | elixir: 1.16.3 25 | 26 | env: 27 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 28 | MIX_ENV: test 29 | 30 | steps: 31 | - name: Clone repo 32 | uses: actions/checkout@v4 33 | 34 | - name: Install OTP and Elixir 35 | uses: erlef/setup-beam@v1 36 | with: 37 | otp-version: ${{matrix.otp}} 38 | elixir-version: ${{matrix.elixir}} 39 | 40 | - name: Cache built dependencies 41 | id: cache-deps 42 | uses: actions/cache@v4 43 | with: 44 | path: | 45 | deps 46 | _build 47 | key: ${{ runner.os }}-mix-otp${{ matrix.otp }}-elixir${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }} 48 | 49 | - name: Install and compile dependencies 50 | if: steps.cache-deps.outputs.cache-hit != 'true' 51 | run: mix do deps.get --only test, deps.compile 52 | 53 | - name: Check formatting 54 | run: mix format --check-formatted 55 | if: ${{matrix.lint}} 56 | 57 | - name: Credo 58 | run: mix credo --strict 59 | if: ${{matrix.lint}} 60 | 61 | - name: Dialyzer 62 | run: mix dialyzer 63 | if: ${{matrix.lint}} 64 | 65 | - name: Check no unused dependencies 66 | run: mix deps.get && mix deps.unlock --check-unused 67 | if: ${{matrix.lint == 'true' && steps.cache-deps.outputs.cache-hit != 'true'}} 68 | 69 | - name: Compile with --warnings-as-errors 70 | run: mix compile --warnings-as-errors 71 | if: ${{matrix.lint}} 72 | 73 | - name: Run tests 74 | run: mix test --trace 75 | -------------------------------------------------------------------------------- /native/rhai_rustler/src/ast.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Mutex; 2 | 3 | use rhai::AST; 4 | use rustler::{Resource, ResourceArc}; 5 | 6 | pub struct ASTResource { 7 | pub ast: Mutex, 8 | } 9 | 10 | #[rustler::resource_impl] 11 | impl Resource for ASTResource {} 12 | 13 | #[rustler::nif] 14 | fn ast_empty() -> ResourceArc { 15 | ResourceArc::new(ASTResource { 16 | ast: Mutex::new(AST::empty()), 17 | }) 18 | } 19 | 20 | #[rustler::nif] 21 | fn ast_set_source(resource: ResourceArc, source: &str) { 22 | let mut ast = resource.ast.try_lock().unwrap(); 23 | 24 | ast.set_source(source); 25 | } 26 | 27 | #[rustler::nif] 28 | fn ast_clear_source(resource: ResourceArc) { 29 | let mut ast = resource.ast.try_lock().unwrap(); 30 | 31 | ast.clear_source(); 32 | } 33 | 34 | #[rustler::nif] 35 | fn ast_source(resource: ResourceArc) -> Option { 36 | let ast = resource.ast.try_lock().unwrap(); 37 | 38 | ast.source().map(|s| s.to_string()) 39 | } 40 | 41 | #[rustler::nif] 42 | fn ast_merge( 43 | resource: ResourceArc, 44 | other_resource: ResourceArc, 45 | ) -> ResourceArc { 46 | let ast = resource.ast.try_lock().unwrap(); 47 | let other_ast = other_resource.ast.try_lock().unwrap(); 48 | 49 | ResourceArc::new(ASTResource { 50 | ast: Mutex::new(ast.merge(&other_ast)), 51 | }) 52 | } 53 | 54 | #[rustler::nif] 55 | fn ast_combine( 56 | resource: ResourceArc, 57 | other_resource: ResourceArc, 58 | ) -> ResourceArc { 59 | let mut ast = resource.ast.try_lock().unwrap(); 60 | let other_ast = other_resource.ast.try_lock().unwrap().clone(); 61 | 62 | ResourceArc::new(ASTResource { 63 | ast: Mutex::new(ast.combine(other_ast).clone()), 64 | }) 65 | } 66 | 67 | #[rustler::nif] 68 | fn ast_clear_functions(resource: ResourceArc) { 69 | let mut ast = resource.ast.try_lock().unwrap(); 70 | 71 | ast.clear_functions(); 72 | } 73 | 74 | #[rustler::nif] 75 | fn ast_clear_statements(resource: ResourceArc) { 76 | let mut ast = resource.ast.try_lock().unwrap(); 77 | 78 | ast.clear_statements(); 79 | } 80 | 81 | #[rustler::nif] 82 | fn ast_clone_functions_only(resource: ResourceArc) -> ResourceArc { 83 | let ast = resource.ast.try_lock().unwrap(); 84 | 85 | ResourceArc::new(ASTResource { 86 | ast: Mutex::new(ast.clone_functions_only()), 87 | }) 88 | } 89 | 90 | #[rustler::nif] 91 | fn ast_has_functions(resource: ResourceArc) -> bool { 92 | let ast = resource.ast.try_lock().unwrap(); 93 | 94 | ast.has_functions() 95 | } 96 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Build precompiled NIFs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - "*" 9 | 10 | jobs: 11 | build_release: 12 | name: NIF ${{ matrix.nif }} - ${{ matrix.job.target }} (${{ matrix.job.os }}) 13 | runs-on: ${{ matrix.job.os }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | nif: ["2.17", "2.16", "2.15"] 18 | job: 19 | - { target: arm-unknown-linux-gnueabihf, os: ubuntu-20.04, use-cross: true } 20 | - { target: aarch64-unknown-linux-gnu, os: ubuntu-20.04, use-cross: true } 21 | - { target: aarch64-unknown-linux-musl, os: ubuntu-20.04, use-cross: true } 22 | - { target: aarch64-apple-darwin, os: macos-latest } 23 | - { target: riscv64gc-unknown-linux-gnu, os: ubuntu-20.04, use-cross: true } 24 | - { target: x86_64-apple-darwin, os: macos-latest } 25 | - { target: x86_64-unknown-linux-gnu, os: ubuntu-20.04 } 26 | - { target: x86_64-unknown-linux-musl, os: ubuntu-20.04, use-cross: true } 27 | - { target: x86_64-pc-windows-gnu, os: ubuntu-20.04, use-cross: true } 28 | - { target: x86_64-pc-windows-msvc, os: windows-2019 } 29 | 30 | steps: 31 | - name: Checkout source code 32 | uses: actions/checkout@v4 33 | 34 | - name: Extract project version 35 | shell: bash 36 | run: | 37 | # Get the project version from mix.exs 38 | echo "PROJECT_VERSION=$(sed -n 's/^ @version "\(.*\)"/\1/p' mix.exs | head -n1)" >> $GITHUB_ENV 39 | 40 | - name: Install Rust toolchain 41 | uses: dtolnay/rust-toolchain@stable 42 | with: 43 | toolchain: stable 44 | target: ${{ matrix.job.target }} 45 | 46 | - name: Build the project 47 | id: build-crate 48 | uses: philss/rustler-precompiled-action@v1.1.4 49 | with: 50 | project-name: rhai_rustler 51 | project-version: ${{ env.PROJECT_VERSION }} 52 | target: ${{ matrix.job.target }} 53 | nif-version: ${{ matrix.nif }} 54 | use-cross: ${{ matrix.job.use-cross }} 55 | project-dir: "native/rhai_rustler" 56 | 57 | - name: Artifact upload 58 | uses: actions/upload-artifact@v4 59 | with: 60 | name: ${{ steps.build-crate.outputs.file-name }} 61 | path: ${{ steps.build-crate.outputs.file-path }} 62 | 63 | - name: Publish archives and packages 64 | uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 65 | with: 66 | files: | 67 | ${{ steps.build-crate.outputs.file-path }} 68 | if: startsWith(github.ref, 'refs/tags/') 69 | -------------------------------------------------------------------------------- /test/rhai/property_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Rhai.PropertyTest do 2 | use ExUnit.Case 3 | use ExUnitProperties 4 | 5 | alias Rhai.{Engine, Scope} 6 | 7 | describe "type conversion" do 8 | property "should convert integer() to rhai integer type and vice-versa" do 9 | engine = Engine.new() 10 | 11 | check all int <- integer() do 12 | scope = Scope.new() |> Scope.push("a", int) 13 | assert {:ok, result} = Engine.eval_with_scope(engine, scope, "a") 14 | assert int == result 15 | assert is_integer(result) 16 | end 17 | end 18 | 19 | property "should convert float() to rhai float type and vice-versa" do 20 | engine = Engine.new() 21 | 22 | check all float <- float() do 23 | scope = Scope.new() |> Scope.push("a", float) 24 | assert {:ok, result} = Engine.eval_with_scope(engine, scope, "a") 25 | assert float == result 26 | assert is_float(result) 27 | end 28 | end 29 | 30 | property "should convert boolean() to rhai bool type and vice-versa" do 31 | engine = Engine.new() 32 | 33 | check all bool <- boolean() do 34 | scope = Scope.new() |> Scope.push("a", bool) 35 | assert {:ok, result} = Engine.eval_with_scope(engine, scope, "a") 36 | assert bool == result 37 | assert is_boolean(result) 38 | end 39 | end 40 | 41 | property "should convert tuple() to rhai array type and vice-versa" do 42 | engine = Engine.new() 43 | 44 | check all tuple <- tuple({integer(), string(:ascii)}) do 45 | scope = Scope.new() |> Scope.push("a", tuple) 46 | assert {:ok, result} = Engine.eval_with_scope(engine, scope, "a") 47 | assert Tuple.to_list(tuple) == result 48 | assert is_list(result) 49 | end 50 | end 51 | 52 | property "should convert list() to rhai array type and vice-versa" do 53 | engine = Engine.new() 54 | 55 | check all list <- list_of(string(:ascii)) do 56 | scope = Scope.new() |> Scope.push("a", list) 57 | assert {:ok, result} = Engine.eval_with_scope(engine, scope, "a") 58 | assert list == result 59 | assert is_list(list) 60 | end 61 | end 62 | 63 | property "should convert String.t() to rhai string type and vice-versa" do 64 | engine = Engine.new() 65 | 66 | check all str1 <- string(:ascii), 67 | str2 <- string(:printable) do 68 | scope = Scope.new() |> Scope.push("a", str1) |> Scope.push("b", str2) 69 | assert {:ok, result} = Engine.eval_with_scope(engine, scope, "a + b") 70 | assert str1 <> str2 == result 71 | assert is_binary(result) 72 | end 73 | end 74 | 75 | property "should convert Map.t() with String.t() keys to rhai object map type and vice-versa" do 76 | engine = Engine.new() 77 | 78 | check all map1 <- map_of(string(:ascii), string(:ascii)), 79 | map2 <- map_of(string(:ascii), integer()), 80 | map3 <- map_of(string(:ascii), map_of(string(:ascii), integer())) do 81 | scope = 82 | Scope.new() |> Scope.push("a", map1) |> Scope.push("b", map2) |> Scope.push("c", map3) 83 | 84 | assert {:ok, result} = Engine.eval_with_scope(engine, scope, "a") 85 | assert map1 == result 86 | 87 | assert {:ok, result} = Engine.eval_with_scope(engine, scope, "b") 88 | assert map2 == result 89 | 90 | assert {:ok, result} = Engine.eval_with_scope(engine, scope, "c") 91 | assert map3 == result 92 | end 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /native/rhai_rustler/src/types.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use rhai::Dynamic; 4 | use rustler::{types::tuple::get_tuple, Encoder, Env, Term, TermType}; 5 | 6 | pub fn from_dynamic(env: Env, value: Dynamic) -> Term { 7 | match value.type_name() { 8 | "()" => rustler::types::atom::nil().to_term(env), 9 | "i64" => value.cast::().encode(env), 10 | "f64" => value.cast::().encode(env), 11 | "bool" => value.cast::().encode(env), 12 | "string" => value.cast::().encode(env), 13 | "char" => value.cast::().to_string().encode(env), 14 | "array" => value 15 | .cast::>() 16 | .into_iter() 17 | .map(|v| from_dynamic(env, v)) 18 | .collect::>() 19 | .encode(env), 20 | "map" => { 21 | let mut map: HashMap = HashMap::new(); 22 | for (k, v) in value.cast::() { 23 | map.insert(k.into(), from_dynamic(env, v)); 24 | } 25 | map.encode(env) 26 | } 27 | _ => ().encode(env), 28 | } 29 | } 30 | 31 | pub fn to_dynamic<'a>(env: Env<'a>, term: &Term<'a>) -> Dynamic { 32 | match Term::get_type(*term) { 33 | TermType::Binary => term 34 | .decode::() 35 | .map(Dynamic::from) 36 | .expect("get_type() returned Binary but could not decode as string."), 37 | 38 | TermType::Atom => term 39 | .decode::() 40 | .map(Dynamic::from) 41 | .or_else(|_| { 42 | if *term == rustler::types::atom::nil().to_term(env) { 43 | Ok(Dynamic::from(())) 44 | } else { 45 | term.atom_to_string().map(Dynamic::from) 46 | } 47 | }) 48 | .expect("get_type() returned Atom but could not decode as string, boolean or empty."), 49 | TermType::Fun => Dynamic::from(()), 50 | TermType::List => { 51 | let items: Vec = term 52 | .decode::>() 53 | .expect("get_type() returned List but could not decode as list.") 54 | .iter() 55 | .map(|item| to_dynamic(env, item)) 56 | .collect(); 57 | 58 | Dynamic::from_array(items) 59 | } 60 | TermType::Map => { 61 | let mut object_map = rhai::Map::new(); 62 | 63 | for (k, v) in term 64 | .decode::>() 65 | .expect("get_type() returned Map but could not decod Hashmap.") 66 | { 67 | object_map.insert(k.into(), to_dynamic(env, &v)); 68 | } 69 | Dynamic::from(object_map) 70 | } 71 | TermType::Float => term 72 | .decode::() 73 | .map(Dynamic::from) 74 | .expect("get_type() returned Float but could not decode as float."), 75 | TermType::Integer => term 76 | .decode::() 77 | .map(Dynamic::from) 78 | .expect("get_type() returned Integer but could not decode as integer."), 79 | TermType::Pid => Dynamic::from(()), 80 | TermType::Port => Dynamic::from(()), 81 | TermType::Ref => Dynamic::from(()), 82 | TermType::Tuple => { 83 | let items: Vec = get_tuple(*term) 84 | .expect("get_type() returned Tuple but could not decode as list.") 85 | .iter() 86 | .map(|item| to_dynamic(env, item)) 87 | .collect(); 88 | 89 | Dynamic::from(items) 90 | } 91 | 92 | TermType::Unknown => Dynamic::from(()), 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /test/rhai/ast_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Rhai.ASTTest do 2 | use ExUnit.Case 3 | 4 | alias Rhai.{AST, Engine} 5 | 6 | describe "empty/0" do 7 | test "should create an empty AST" do 8 | assert %AST{} = ast = AST.empty() 9 | assert nil == AST.source(ast) 10 | end 11 | end 12 | 13 | describe "set_source/2" do 14 | test "should set the source" do 15 | assert "x + 1;" == 16 | AST.empty() 17 | |> AST.set_source("x + 1;") 18 | |> AST.source() 19 | end 20 | end 21 | 22 | describe "clear_source/1" do 23 | test "should clear the source" do 24 | assert nil == 25 | AST.empty() 26 | |> AST.set_source("x + 1;") 27 | |> AST.clear_source() 28 | |> AST.source() 29 | end 30 | end 31 | 32 | describe "merge/2" do 33 | test "should merge two ASTs" do 34 | engine = Engine.new() 35 | 36 | {:ok, ast1} = 37 | Engine.compile(engine, """ 38 | fn foo(x) { 42 + x } 39 | foo(1) 40 | """) 41 | 42 | {:ok, ast2} = 43 | Engine.compile(engine, """ 44 | fn foo(n) { `hello${n}` } 45 | foo("!") 46 | """) 47 | 48 | ast3 = AST.merge(ast1, ast2) 49 | 50 | assert {:ok, "hello!"} == 51 | Engine.eval_ast(engine, ast3) 52 | end 53 | end 54 | 55 | describe "combine/2" do 56 | test "should combine two ASTs" do 57 | engine = Engine.new() 58 | 59 | {:ok, ast1} = 60 | Engine.compile(engine, """ 61 | fn foo(x) { 42 + x } 62 | foo(1) 63 | """) 64 | 65 | {:ok, ast2} = 66 | Engine.compile(engine, """ 67 | fn foo(n) { `hello${n}` } 68 | foo("!") 69 | """) 70 | 71 | ast3 = AST.combine(ast1, ast2) 72 | 73 | assert {:ok, "hello!"} == 74 | Engine.eval_ast(engine, ast3) 75 | end 76 | end 77 | 78 | describe "clear_functions/1" do 79 | test "should clear all functions" do 80 | engine = Engine.new() 81 | 82 | {:ok, ast} = 83 | Engine.compile(engine, """ 84 | fn foo(x) { 42 + x } 85 | foo(1) 86 | """) 87 | 88 | assert {:ok, 43} == 89 | Engine.eval_ast(engine, ast) 90 | 91 | ast = AST.clear_functions(ast) 92 | 93 | assert {:error, {:function_not_found, _}} = Engine.eval_ast(engine, ast) 94 | end 95 | end 96 | 97 | describe "clear_statements/1" do 98 | test "should clear all statements" do 99 | engine = Engine.new() 100 | 101 | {:ok, ast} = 102 | Engine.compile(engine, """ 103 | 1 + 1 104 | """) 105 | 106 | assert {:ok, 2} == 107 | Engine.eval_ast(engine, ast) 108 | 109 | ast = AST.clear_statements(ast) 110 | 111 | assert {:ok, nil} = Engine.eval_ast(engine, ast) 112 | end 113 | end 114 | 115 | describe "clone_functions_only/1" do 116 | test "should clone all functions" do 117 | engine = Engine.new() 118 | 119 | {:ok, ast} = 120 | Engine.compile(engine, """ 121 | fn foo(x) { 42 + x } 122 | foo(1) 123 | """) 124 | 125 | assert {:ok, 43} == 126 | Engine.eval_ast(engine, ast) 127 | 128 | ast = AST.clone_functions_only(ast) 129 | 130 | assert {:ok, nil} = Engine.eval_ast(engine, ast) 131 | assert AST.has_functions?(ast) 132 | 133 | {:ok, ast2} = Engine.compile(engine, "foo(1)") 134 | 135 | ast = AST.merge(ast, ast2) 136 | 137 | assert {:ok, 43} = Engine.eval_ast(engine, ast) 138 | end 139 | end 140 | 141 | describe "has_functions?" do 142 | test "should return true if the AST has functions" do 143 | engine = Engine.new() 144 | 145 | {:ok, ast} = 146 | Engine.compile(engine, """ 147 | fn foo(x) { 42 + x } 148 | foo(1) 149 | """) 150 | 151 | assert AST.has_functions?(ast) 152 | end 153 | 154 | test "should return false if the AST has no functions" do 155 | engine = Engine.new() 156 | 157 | {:ok, ast} = 158 | Engine.compile(engine, """ 159 | 1 + 1 160 | """) 161 | 162 | assert not AST.has_functions?(ast) 163 | end 164 | end 165 | end 166 | -------------------------------------------------------------------------------- /lib/rhai/ast.ex: -------------------------------------------------------------------------------- 1 | defmodule Rhai.AST do 2 | @moduledoc """ 3 | Compiled AST (abstract syntax tree) of a Rhai script. 4 | """ 5 | 6 | defstruct [ 7 | # The actual NIF Resource. 8 | resource: nil, 9 | 10 | # Normally the compiler will happily do stuff like inlining the 11 | # resource in attributes. This will convert the resource into an 12 | # empty binary with no warning. This will make that harder to 13 | # accidentaly do. 14 | # It also serves as a handy way to tell file handles apart. 15 | reference: nil 16 | ] 17 | 18 | @type t :: %__MODULE__{} 19 | 20 | @doc """ 21 | Create an empty AST. 22 | """ 23 | def empty do 24 | resource = Rhai.Native.ast_empty() 25 | 26 | wrap_resource(resource) 27 | end 28 | 29 | @doc """ 30 | Get the source if any. 31 | """ 32 | @spec source(t()) :: String.t() | nil 33 | def source(%__MODULE__{resource: resource}) do 34 | Rhai.Native.ast_source(resource) 35 | end 36 | 37 | @doc """ 38 | Set the source. 39 | """ 40 | @spec set_source(t(), String.t()) :: t() 41 | def set_source(%__MODULE__{resource: resource} = ast, source) do 42 | Rhai.Native.ast_set_source(resource, source) 43 | 44 | ast 45 | end 46 | 47 | @doc """ 48 | Clear the source. 49 | """ 50 | @spec clear_source(t()) :: t() 51 | def clear_source(%__MODULE__{resource: resource} = ast) do 52 | Rhai.Native.ast_clear_source(resource) 53 | 54 | ast 55 | end 56 | 57 | @doc """ 58 | Merge two AST into one. Both AST’s are untouched and a new, merged, version is returned. 59 | 60 | Statements in the second AST are simply appended to the end of the first without any processing. 61 | Thus, the return value of the first AST (if using expression-statement syntax) is buried. 62 | Of course, if the first AST uses a return statement at the end, then the second AST will essentially be dead code. 63 | 64 | All script-defined functions in the second AST overwrite similarly-named functions in the first AST with the same number of parameters. 65 | 66 | See [example](https://docs.rs/rhai/latest/rhai/struct.AST.html#example-1) in the Rhai documentation. 67 | """ 68 | @spec merge(t(), t()) :: t() 69 | def merge( 70 | %__MODULE__{resource: resource}, 71 | %__MODULE__{resource: other_resource} 72 | ) do 73 | resource 74 | |> Rhai.Native.ast_merge(other_resource) 75 | |> wrap_resource() 76 | end 77 | 78 | @doc """ 79 | Combine one AST with another. The second AST is consumed. 80 | 81 | Statements in the second AST are simply appended to the end of the first without any processing. 82 | Thus, the return value of the first AST (if using expression-statement syntax) is buried. 83 | Of course, if the first AST uses a return statement at the end, then the second AST will essentially be dead code. 84 | 85 | All script-defined functions in the second AST overwrite similarly-named functions in the first AST with the same number of parameters. 86 | 87 | See [example](https://docs.rs/rhai/latest/rhai/struct.AST.html#example-2) in the Rhai documentation. 88 | """ 89 | @spec combine(t(), t()) :: t() 90 | def combine( 91 | %__MODULE__{resource: resource}, 92 | %__MODULE__{resource: other_resource} 93 | ) do 94 | resource 95 | |> Rhai.Native.ast_combine(other_resource) 96 | |> wrap_resource() 97 | end 98 | 99 | @doc """ 100 | Clear all function definitions in the AST. 101 | """ 102 | @spec clear_functions(t()) :: t() 103 | def clear_functions(%__MODULE__{resource: resource} = ast) do 104 | Rhai.Native.ast_clear_functions(resource) 105 | 106 | ast 107 | end 108 | 109 | @doc """ 110 | Clear all statements in the AST, leaving only function definitions. 111 | """ 112 | @spec clear_statements(t()) :: t() 113 | def clear_statements(%__MODULE__{resource: resource} = ast) do 114 | Rhai.Native.ast_clear_statements(resource) 115 | 116 | ast 117 | end 118 | 119 | @doc """ 120 | Clone the AST’s functions into a new AST. No statements are cloned. 121 | """ 122 | @spec clone_functions_only(t()) :: t() 123 | def clone_functions_only(%__MODULE__{resource: resource}) do 124 | resource 125 | |> Rhai.Native.ast_clone_functions_only() 126 | |> wrap_resource() 127 | end 128 | 129 | @doc """ 130 | Does this AST contain script-defined functions? 131 | """ 132 | @spec has_functions?(t()) :: bool 133 | def has_functions?(%__MODULE__{resource: resource}) do 134 | Rhai.Native.ast_has_functions(resource) 135 | end 136 | 137 | @doc false 138 | def wrap_resource(resource) do 139 | %__MODULE__{ 140 | resource: resource, 141 | reference: make_ref() 142 | } 143 | end 144 | end 145 | -------------------------------------------------------------------------------- /native/rhai_rustler/src/scope.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Mutex; 2 | 3 | use rhai::Scope; 4 | use rustler::{Encoder, Env, Resource, ResourceArc, Term}; 5 | 6 | use crate::{ 7 | error::{RhaiRustlerError, ScopeError}, 8 | types::{from_dynamic, to_dynamic}, 9 | }; 10 | 11 | pub struct ScopeResource { 12 | pub scope: Mutex>, 13 | } 14 | 15 | #[rustler::resource_impl] 16 | impl Resource for ScopeResource {} 17 | 18 | #[rustler::nif] 19 | fn scope_new() -> ResourceArc { 20 | ResourceArc::new(ScopeResource { 21 | scope: Mutex::new(Scope::new()), 22 | }) 23 | } 24 | 25 | #[rustler::nif] 26 | fn scope_with_capacity(capacity: usize) -> ResourceArc { 27 | ResourceArc::new(ScopeResource { 28 | scope: Mutex::new(Scope::with_capacity(capacity)), 29 | }) 30 | } 31 | 32 | #[rustler::nif] 33 | fn scope_push_dynamic<'a>( 34 | env: Env<'a>, 35 | resource: ResourceArc, 36 | name: &str, 37 | value: Term<'a>, 38 | ) { 39 | let mut scope = resource.scope.try_lock().unwrap(); 40 | 41 | scope.push_dynamic(name, to_dynamic(env, &value)); 42 | } 43 | 44 | #[rustler::nif] 45 | fn scope_push_constant_dynamic<'a>( 46 | env: Env<'a>, 47 | resource: ResourceArc, 48 | name: &str, 49 | value: Term<'a>, 50 | ) { 51 | let mut scope = resource.scope.try_lock().unwrap(); 52 | 53 | scope.push_constant_dynamic(name, to_dynamic(env, &value)); 54 | } 55 | 56 | #[rustler::nif] 57 | fn scope_contains(resource: ResourceArc, name: &str) -> bool { 58 | let scope = resource.scope.try_lock().unwrap(); 59 | 60 | scope.contains(name) 61 | } 62 | 63 | #[rustler::nif] 64 | fn scope_is_constant(resource: ResourceArc, name: &str) -> Option { 65 | let scope = resource.scope.try_lock().unwrap(); 66 | 67 | scope.is_constant(name) 68 | } 69 | 70 | #[rustler::nif] 71 | fn scope_get_value<'a>( 72 | env: Env<'a>, 73 | resource: ResourceArc, 74 | name: &str, 75 | ) -> Option> { 76 | let scope = resource.scope.try_lock().unwrap(); 77 | 78 | scope.get_value(name).map(|v| from_dynamic(env, v)) 79 | } 80 | 81 | #[rustler::nif] 82 | fn scope_clear(resource: ResourceArc) { 83 | let mut scope = resource.scope.try_lock().unwrap(); 84 | 85 | scope.clear(); 86 | } 87 | 88 | #[rustler::nif] 89 | fn scope_clone_visible(resource: ResourceArc) -> ResourceArc { 90 | let scope = resource.scope.try_lock().unwrap(); 91 | 92 | ResourceArc::new(ScopeResource { 93 | scope: Mutex::new(scope.clone_visible()), 94 | }) 95 | } 96 | 97 | #[rustler::nif] 98 | fn scope_is_empty(resource: ResourceArc) -> bool { 99 | let scope = resource.scope.try_lock().unwrap(); 100 | 101 | scope.is_empty() 102 | } 103 | 104 | #[rustler::nif] 105 | fn scope_len(resource: ResourceArc) -> usize { 106 | let scope = resource.scope.try_lock().unwrap(); 107 | 108 | scope.len() 109 | } 110 | 111 | #[rustler::nif] 112 | fn scope_remove<'a>( 113 | env: Env<'a>, 114 | resource: ResourceArc, 115 | name: &str, 116 | ) -> Option> { 117 | let mut scope = resource.scope.try_lock().unwrap(); 118 | 119 | scope.remove(name).map(|v| from_dynamic(env, v)) 120 | } 121 | 122 | #[rustler::nif] 123 | fn scope_rewind(resource: ResourceArc, size: usize) { 124 | let mut scope = resource.scope.try_lock().unwrap(); 125 | 126 | scope.rewind(size); 127 | } 128 | 129 | #[rustler::nif] 130 | fn scope_iter_collect<'a>(env: Env<'a>, resource: ResourceArc) -> Vec> { 131 | let scope = resource.scope.try_lock().unwrap(); 132 | let value: Vec> = scope 133 | .iter() 134 | .map(|(n, _, v)| (n, from_dynamic(env, v)).encode(env)) 135 | .collect(); 136 | 137 | value 138 | } 139 | 140 | #[rustler::nif] 141 | fn scope_pop(resource: ResourceArc) -> Result<(), RhaiRustlerError> { 142 | let mut scope = resource.scope.try_lock().unwrap(); 143 | 144 | if scope.is_empty() { 145 | return Err(ScopeError::ErrorScopeIsEmpty.into()); 146 | } 147 | scope.pop(); 148 | 149 | Ok(()) 150 | } 151 | 152 | #[rustler::nif] 153 | fn scope_set_value<'a>( 154 | env: Env<'a>, 155 | resource: ResourceArc, 156 | name: &str, 157 | value: Term<'a>, 158 | ) -> Result<(), RhaiRustlerError> { 159 | let mut scope = resource.scope.try_lock().unwrap(); 160 | 161 | if scope.is_constant(name).unwrap_or(false) { 162 | return Err(ScopeError::ErrorCannotUpdateValueOfConstant.into()); 163 | } 164 | scope.set_value(name, to_dynamic(env, &value)); 165 | 166 | Ok(()) 167 | } 168 | 169 | #[rustler::nif] 170 | fn scope_set_or_push<'a>( 171 | env: Env<'a>, 172 | resource: ResourceArc, 173 | name: &str, 174 | value: Term<'a>, 175 | ) { 176 | let mut scope = resource.scope.try_lock().unwrap(); 177 | 178 | scope.set_or_push(name, to_dynamic(env, &value)); 179 | } 180 | -------------------------------------------------------------------------------- /native/rhai_rustler/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::panic::RefUnwindSafe; 2 | 3 | use thiserror::Error; 4 | 5 | use rhai::{EvalAltResult, ParseError}; 6 | use rustler::{Encoder, Env, Term}; 7 | mod atoms { 8 | rustler::atoms! { 9 | system, 10 | parsing, 11 | variable_exists, 12 | forbidden_variable, 13 | variable_not_found, 14 | property_not_found, 15 | index_not_found, 16 | function_not_found, 17 | module_not_found, 18 | in_function_call, 19 | in_module, 20 | unbound_this, 21 | mismatch_data_type, 22 | mismatch_output_type, 23 | indexing_type, 24 | array_bounds, 25 | string_bounds, 26 | bit_field_bounds, 27 | for_atom = "for", 28 | data_race, 29 | assignment_to_constant, 30 | dot_expr, 31 | arithmetic, 32 | too_many_operations, 33 | too_many_modules, 34 | stack_overflow, 35 | data_too_large, 36 | terminated, 37 | custom_syntax, 38 | runtime, 39 | non_pure_method_call_on_constant, 40 | scope_is_empty, 41 | cannot_update_value_of_constant, 42 | custom_operator 43 | } 44 | } 45 | 46 | #[derive(Error, Debug)] 47 | pub enum ScopeError { 48 | #[error("The Scope is empty.")] 49 | ErrorScopeIsEmpty, 50 | #[error("Cannot update the value of a constant.")] 51 | ErrorCannotUpdateValueOfConstant, 52 | } 53 | 54 | #[derive(Error, Debug)] 55 | #[error(transparent)] 56 | pub struct EvaluationError(pub Box); 57 | 58 | impl RefUnwindSafe for EvaluationError {} 59 | 60 | impl From> for RhaiRustlerError { 61 | fn from(err: Box) -> Self { 62 | RhaiRustlerError::Evaluation(EvaluationError(err)) 63 | } 64 | } 65 | 66 | #[derive(Error, Debug)] 67 | pub enum RhaiRustlerError { 68 | #[error("Error in evaluation: {0}.")] 69 | Evaluation(#[from] EvaluationError), 70 | #[error("Error when parsing a script: {0}.")] 71 | Parse(#[from] ParseError), 72 | #[error("Error when accessing a scope: {0}.")] 73 | Scope(#[from] ScopeError), 74 | #[error("Error when defining a custom operator: {message}.")] 75 | CustomOperator { message: String }, 76 | } 77 | 78 | impl Encoder for RhaiRustlerError { 79 | fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { 80 | match self { 81 | RhaiRustlerError::Evaluation(EvaluationError(err)) => { 82 | let error_atom = match err.unwrap_inner() { 83 | EvalAltResult::ErrorSystem(_, _) => atoms::system(), 84 | EvalAltResult::ErrorParsing(_, _) => atoms::parsing(), 85 | EvalAltResult::ErrorVariableExists(_, _) => atoms::variable_exists(), 86 | EvalAltResult::ErrorForbiddenVariable(_, _) => atoms::forbidden_variable(), 87 | EvalAltResult::ErrorVariableNotFound(_, _) => atoms::variable_not_found(), 88 | EvalAltResult::ErrorPropertyNotFound(_, _) => atoms::property_not_found(), 89 | EvalAltResult::ErrorIndexNotFound(_, _) => atoms::index_not_found(), 90 | EvalAltResult::ErrorFunctionNotFound(_, _) => atoms::function_not_found(), 91 | EvalAltResult::ErrorModuleNotFound(_, _) => atoms::module_not_found(), 92 | EvalAltResult::ErrorInFunctionCall(_, _, _, _) => atoms::in_function_call(), 93 | EvalAltResult::ErrorInModule(_, _, _) => atoms::in_module(), 94 | EvalAltResult::ErrorUnboundThis(_) => atoms::unbound_this(), 95 | EvalAltResult::ErrorMismatchDataType(_, _, _) => atoms::mismatch_data_type(), 96 | EvalAltResult::ErrorMismatchOutputType(_, _, _) => { 97 | atoms::mismatch_output_type() 98 | } 99 | EvalAltResult::ErrorIndexingType(_, _) => atoms::indexing_type(), 100 | EvalAltResult::ErrorArrayBounds(_, _, _) => atoms::array_bounds(), 101 | EvalAltResult::ErrorStringBounds(_, _, _) => atoms::string_bounds(), 102 | EvalAltResult::ErrorBitFieldBounds(_, _, _) => atoms::bit_field_bounds(), 103 | EvalAltResult::ErrorFor(_) => atoms::for_atom(), 104 | EvalAltResult::ErrorDataRace(_, _) => atoms::data_race(), 105 | EvalAltResult::ErrorAssignmentToConstant(_, _) => { 106 | atoms::assignment_to_constant() 107 | } 108 | EvalAltResult::ErrorDotExpr(_, _) => atoms::dot_expr(), 109 | EvalAltResult::ErrorArithmetic(_, _) => atoms::arithmetic(), 110 | EvalAltResult::ErrorTooManyOperations(_) => atoms::too_many_operations(), 111 | EvalAltResult::ErrorTooManyModules(_) => atoms::too_many_modules(), 112 | EvalAltResult::ErrorStackOverflow(_) => atoms::stack_overflow(), 113 | EvalAltResult::ErrorDataTooLarge(_, _) => atoms::data_too_large(), 114 | EvalAltResult::ErrorTerminated(_, _) => atoms::terminated(), 115 | EvalAltResult::ErrorCustomSyntax(_, _, _) => atoms::custom_syntax(), 116 | EvalAltResult::ErrorRuntime(_, _) => atoms::runtime(), 117 | EvalAltResult::ErrorNonPureMethodCallOnConstant(_, _) => { 118 | atoms::non_pure_method_call_on_constant() 119 | } 120 | _ => panic!("Not an error"), 121 | }; 122 | 123 | make_reason_tuple(env, error_atom, err.to_string()) 124 | } 125 | RhaiRustlerError::Parse(err) => { 126 | make_reason_tuple(env, atoms::parsing(), err.to_string()) 127 | } 128 | RhaiRustlerError::Scope(err) => { 129 | let error_atom = match err { 130 | ScopeError::ErrorScopeIsEmpty => atoms::scope_is_empty(), 131 | ScopeError::ErrorCannotUpdateValueOfConstant => { 132 | atoms::cannot_update_value_of_constant() 133 | } 134 | }; 135 | 136 | make_reason_tuple(env, error_atom, err.to_string()) 137 | } 138 | RhaiRustlerError::CustomOperator { message } => { 139 | make_reason_tuple(env, atoms::custom_operator(), message.to_owned()) 140 | } 141 | } 142 | } 143 | } 144 | 145 | fn make_reason_tuple(env: Env, atom: rustler::types::atom::Atom, err_str: String) -> Term { 146 | (atom, err_str.encode(env)).encode(env) 147 | } 148 | -------------------------------------------------------------------------------- /lib/rhai/native.ex: -------------------------------------------------------------------------------- 1 | defmodule Rhai.Native do 2 | @moduledoc false 3 | 4 | version = Mix.Project.config()[:version] 5 | 6 | targets = ~w( 7 | aarch64-apple-darwin 8 | x86_64-apple-darwin 9 | x86_64-unknown-linux-gnu 10 | x86_64-unknown-linux-musl 11 | arm-unknown-linux-gnueabihf 12 | aarch64-unknown-linux-gnu 13 | aarch64-unknown-linux-musl 14 | x86_64-pc-windows-msvc 15 | x86_64-pc-windows-gnu 16 | ) 17 | 18 | use RustlerPrecompiled, 19 | otp_app: :rhai_rustler, 20 | crate: "rhai_rustler", 21 | base_url: "https://github.com/rhaiscript/rhai_rustler/releases/download/v#{version}", 22 | force_build: 23 | Application.compile_env(:rustler_precompiled, [:force_build, :rhai_rustler], false) || 24 | System.get_env("RHAI_RUSTLER_FORCE_BUILD") in ["1", "true"], 25 | version: version, 26 | targets: targets 27 | 28 | # engine 29 | def engine_new, do: err() 30 | def engine_new_raw, do: err() 31 | def engine_set_module_resolvers(_engine, _module_resolvers), do: err() 32 | def engine_register_global_module(_engine, _path), do: err() 33 | def engine_register_static_module(_engine, _namespace, _path), do: err() 34 | def engine_register_custom_operator(_engine, _keyword, _precedence), do: err() 35 | def engine_register_package(_engine, _package), do: err() 36 | def engine_compile(_engine, _script), do: err() 37 | def engine_compile_with_scope(_engine, _scope, _script), do: err() 38 | def engine_compile_expression(_engine, _script), do: err() 39 | def engine_compile_expression_with_scope(_engine, _scope, _script), do: err() 40 | def engine_compile_file(_engine, _path), do: err() 41 | def engine_compile_file_with_scope(_engine, _scope, _path), do: err() 42 | def engine_compile_into_self_contained(_engine, _scope, _script), do: err() 43 | def engine_compile_scripts_with_scope(_engine, _scope, _scripts), do: err() 44 | def engine_compact_script(_engine, _script), do: err() 45 | def engine_eval(_engine, _script), do: err() 46 | def engine_eval_with_scope(_engine, _scope, _script), do: err() 47 | def engine_eval_ast(_engine, _ast), do: err() 48 | def engine_eval_ast_with_scope(_engine, _scope, _ast), do: err() 49 | def engine_eval_expression(_engine, _script), do: err() 50 | def engine_eval_expression_with_scope(_engine, _scope, _script), do: err() 51 | def engine_eval_file(_engine, _path), do: err() 52 | def engine_eval_file_with_scope(_engine, _scope, _path), do: err() 53 | def engine_run(_engine, _script), do: err() 54 | def engine_run_with_scope(_engine, _scope, _script), do: err() 55 | def engine_run_ast(_engine, _ast), do: err() 56 | def engine_run_ast_with_scope(_engine, _scope, _ast), do: err() 57 | def engine_run_file(_engine, _path), do: err() 58 | def engine_run_file_with_scope(_engine, _scope, _path), do: err() 59 | def engine_call_fn(_engine, _scope, _ast, _name, _args), do: err() 60 | def engine_set_fail_on_invalid_map_property(_engine, _flag), do: err() 61 | def engine_fail_on_invalid_map_property(_engine), do: err() 62 | def engine_set_max_array_size(_engine, _flag), do: err() 63 | def engine_max_array_size(_engine), do: err() 64 | def engine_set_allow_anonymous_fn(_engine, _flag), do: err() 65 | def engine_allow_anonymous_fn(_engine), do: err() 66 | def engine_set_allow_if_expression(_engine, _flag), do: err() 67 | def engine_allow_if_expression(_engine), do: err() 68 | def engine_set_allow_loop_expressions(_engine, _flag), do: err() 69 | def engine_allow_loop_expressions(_engine), do: err() 70 | def engine_set_allow_looping(_engine, _flag), do: err() 71 | def engine_allow_looping(_engine), do: err() 72 | def engine_set_allow_shadowing(_engine, _flag), do: err() 73 | def engine_allow_shadowing(_engine), do: err() 74 | def engine_set_allow_statement_expression(_engine, _flag), do: err() 75 | def engine_allow_statement_expression(_engine), do: err() 76 | def engine_set_allow_switch_expression(_engine, _flag), do: err() 77 | def engine_allow_switch_expression(_engine), do: err() 78 | def engine_set_fast_operators(_engine, _flag), do: err() 79 | def engine_fast_operators(_engine), do: err() 80 | def engine_set_max_call_levels(_engine, _levels), do: err() 81 | def engine_max_call_levels(_engine), do: err() 82 | def engine_set_max_expr_depths(_engine, _max_expr_depth, _max_function_expr_depth), do: err() 83 | def engine_max_expr_depth(_engine), do: err() 84 | def engine_max_function_expr_depth(_engine), do: err() 85 | def engine_set_max_map_size(_engine, _max_size), do: err() 86 | def engine_max_map_size(_engine), do: err() 87 | def engine_set_max_modules(_engine, _modules), do: err() 88 | def engine_max_modules(_engine), do: err() 89 | def engine_set_max_operations(_engine, _operations), do: err() 90 | def engine_max_operations(_engine), do: err() 91 | def engine_set_max_string_size(_engine, _max_len), do: err() 92 | def engine_max_string_size(_engine), do: err() 93 | def engine_set_strict_variables(_engine, _flag), do: err() 94 | def engine_strict_variables(_engine), do: err() 95 | def engine_optimization_level(_engine), do: err() 96 | def engine_set_optimization_level(_engine, _optimization_level), do: err() 97 | def engine_optimize_ast(_engine, _scope, _ast, _optimization_level), do: err() 98 | def engine_disable_symbol(_engine, _symbol), do: err() 99 | def engine_ensure_data_size_within_limits(_engine, _value), do: err() 100 | # scope 101 | def scope_new, do: err() 102 | def scope_with_capacity(_capacity), do: err() 103 | def scope_push_dynamic(_scope, _name, _value), do: err() 104 | def scope_push_constant_dynamic(_scope, _name, _value), do: err() 105 | def scope_contains(_scope, _name), do: err() 106 | def scope_is_constant(_scope, _name), do: err() 107 | def scope_get_value(_scope, _name), do: err() 108 | def scope_clear(_scope), do: err() 109 | def scope_clone_visible(_scope), do: err() 110 | def scope_is_empty(_scope), do: err() 111 | def scope_len(_scope), do: err() 112 | def scope_remove(_scope, _name), do: err() 113 | def scope_rewind(_scope, _size), do: err() 114 | def scope_pop(_scope), do: err() 115 | def scope_set_value(_scope, _name, _value), do: err() 116 | def scope_set_alias(_scope, _name, _alias), do: err() 117 | def scope_set_or_push(_scope, _name, _value), do: err() 118 | def scope_iter_collect(_scope), do: err() 119 | # AST 120 | def ast_empty, do: err() 121 | def ast_source(_ast), do: err() 122 | def ast_set_source(_ast, _source), do: err() 123 | def ast_clear_source(_ast), do: err() 124 | def ast_merge(_ast, _other), do: err() 125 | def ast_combine(_ast, _other), do: err() 126 | def ast_clear_functions(_ast), do: err() 127 | def ast_clear_statements(_ast), do: err() 128 | def ast_clone_functions_only(_ast), do: err() 129 | def ast_has_functions(_ast), do: err() 130 | 131 | defp err, do: :erlang.nif_error(:nif_not_loaded) 132 | end 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rhai_rustler 2 | 3 | [![CI](https://github.com/rhaiscript/rhai_rustler/actions/workflows/main.yaml/badge.svg)](https://github.com/rhaiscript/rhai_rustler/actions/workflows/main.yaml) 4 | [![Rust CI](https://github.com/rhaiscript/rhai_rustler/actions/workflows/rust-ci.yaml/badge.svg)](https://github.com/rhaiscript/rhai_rustler/actions/workflows/rust-ci.yaml) 5 | [![NIFs precompilation](https://github.com/rhaiscript/rhai_rustler/actions/workflows/release.yaml/badge.svg)](https://github.com/rhaiscript/rhai_rustler/actions/workflows/release.yaml) 6 | [![Hex.pm](https://img.shields.io/hexpm/v/rhai_rustler.svg)](https://hex.pm/packages/rhai_rustler) 7 | [![Hex Docs](https://img.shields.io/badge/hex-docs-purple.svg)](https://hexdocs.pm/rhai_rustler/) 8 | 9 | Elixir NIF bindings for Rhai, a tiny, simple and fast embedded scripting language for Rust that gives you a safe and easy way to add scripting to your applications. 10 | 11 | Please refer to [The Rhai Book](https://rhai.rs/book/index.html) for extended information about the language. 12 | 13 | ## Installation 14 | 15 | Add `:rhai_rustler` to the list of dependencies in `mix.exs`: 16 | 17 | ```elixir 18 | def deps do 19 | [ 20 | {:rhai_rustler, "~> 1.0.0"} 21 | ] 22 | end 23 | ``` 24 | 25 | ## Features 26 | 27 | `rhai_rustler` exposes a subset of the Rhai API to Elixir: 28 | 29 | - [Engine]() - [Rhai Book](https://rhai.rs/book/engine/index.html) - [docs.rs](https://docs.rs/rhai/latest/rhai/struct.Engine.html) 30 | - [Scope]() - [Rhai book](https://rhai.rs/book/engine/scope.html) - [docs.rs](https://docs.rs/rhai/latest/rhai/struct.Scope.html) 31 | - [AST]() - [Rhai book](https://rhai.rs/book/engine/ast.html) - [docs.rs](https://docs.rs/rhai/latest/rhai/struct.Ast.html) 32 | 33 | Note that not all the Rhai API features are supported. For instance, advanced and low-level APIs are not exposed. 34 | 35 | If any usage patterns become apparent, they will be included in the future. 36 | 37 | Please refer to [NIF bindings](guides/nif-bindings.md) to see the methods supported by the Elixir NIF. 38 | 39 | The Elixir NIF provides a way to extend Rhai with external native Rust modules, see: [Extending Rhai with external native Rust modules](#extending-rhai-with-external-native-rust-modules) and [rhai_dylib](https://github.com/rhaiscript/rhai-dylib) for more information. 40 | 41 | To check the supported types conversion, see [Type conversion table](#type-conversion-table). 42 | 43 | ## Usage patterns 44 | 45 | ### "Hello Rhai" 46 | 47 | ```elixir 48 | engine = Rhai.Engine.new() 49 | 50 | {:ok, "Hello Rhai!"} = Rhai.Engine.eval(engine, "\"Hello Rhai!\"") 51 | ``` 52 | 53 | ### Eval 54 | 55 | ```elixir 56 | engine = Rhai.Engine.new() 57 | 58 | # Simple evaluation 59 | {:ok, 2} = Rhai.Engine.eval(engine, "1 + 1") 60 | 61 | # Evaluation with scope 62 | scope = Rhai.Scope.new() |> Rhai.Scope.push("a", 10) |> Rhai.Scope.push("b", 3) 63 | {:ok, 30} = Rhai.Engine.eval_with_scope(engine, scope, "a * b") 64 | ``` 65 | 66 | ### AST 67 | 68 | ```elixir 69 | engine = Rhai.Engine.new() 70 | scope = Rhai.Scope.new() |> Rhai.Scope.push_constant("a", 10) |> Rhai.Scope.push_constant("b", 3) 71 | 72 | {:ok, %Rhai.AST{} = ast} = Rhai.Engine.compile_with_scope(engine, scope, "a * b") 73 | {:ok, 30} = Rhai.Engine.eval_ast(engine, ast) 74 | 75 | # AST can be shared between engines 76 | task = Task.async(fn -> Rhai.Engine.eval_ast(Rhai.Engine.new(), ast) end) 77 | {:ok, 30} = Task.await(task) 78 | ``` 79 | 80 | ### Raw Engine 81 | 82 | ```elixir 83 | engine = Rhai.Engine.new_raw() 84 | 85 | # Returns an error since BasicArrayPackage is not registered 86 | {:error, {:function_not_found, _}} = Rhai.Engine.eval(engine, "[1, 2, 3].find(|x| x > 2)") 87 | 88 | # Please refer to https://rhai.rs/book/rust/packages/builtin.html for more information about packages 89 | engine = Rhai.Engine.register_package(engine, :basic_array) 90 | {:ok, 3} = Rhai.Engine.eval(engine, "[1, 2, 3].find(|x| x > 2)") 91 | ``` 92 | 93 | ### Extending rhai_rustler with external native Rust modules 94 | 95 | `rhai_rustler` utilizes the `[rhai_dylib](https://github.com/rhaiscript/rhai-dylib)` library to expand the capabilities of Rhai by loading external native Rust modules. This allows users to introduce new functions, custom types, and operators. 96 | 97 | [test_dylib_module](https://github.com/rhaiscript/rhai_rustler/tree/main/native/test_dylib_module) serves as an example of how to create a dylib module. A [dummy rustler module](https://github.com/rhaiscript/rhai_rustler/blob/main/test/support/test_dylib_module.ex) is employed to trigger the compilation process. This same approach can be adopted in real-world projects, such as when distributing the dylib module as a Hex package. 98 | 99 | ## Type conversion table 100 | 101 | Elixir Types are converted to Rhai types (and back) as follows: 102 | 103 | | Elixir | Rhai | 104 | | ------------------------------- | --------------------- | 105 | | integer() | Integer | 106 | | float() | Float | 107 | | float() | Decimal | 108 | | bool() | Boolean | 109 | | String.t() | String | 110 | | String.t() | Char | 111 | | list() | Array | 112 | | tuple() | Array | 113 | | %{ String.t() => Rhai.Any.t() } | Object map | 114 | | nil() | Empty | 115 | | pid() | Empty (not supported) | 116 | | ref() | Empty (not supported) | 117 | | fun() | Empty (not supported) | 118 | | map() | Empty (not supported) | 119 | 120 | ## Rustler precompiled 121 | 122 | By default, **you don't need the Rust toolchain installed** because the lib will try to download 123 | a precompiled NIF file. 124 | In case you want to force compilation set the 125 | `RHAI_RUSTLER_FORCE_BUILD` environment variable to `true` or `1`. 126 | 127 | Precompiled NIFs are available for the following platforms: 128 | 129 | - aarch64-apple-darwin 130 | - x86_64-apple-darwin 131 | - x86_64-unknown-linux-gnu 132 | - x86_64-unknown-linux-musl 133 | - arm-unknown-linux-gnueabihf 134 | - aarch64-unknown-linux-gnu 135 | - aarch64-unknown-linux-musl 136 | - x86_64-pc-windows-msvc 137 | - x86_64-pc-windows-gnu 138 | 139 | ### Release flow 140 | 141 | Please follow [this guide](https://hexdocs.pm/rustler_precompiled/precompilation_guide.html#the-release-flow) when releasing a new version of the library. 142 | 143 | ## License 144 | 145 | This library is licensed under Apache 2.0 License. See [LICENSE](LICENSE) for details. 146 | 147 | ## Links 148 | 149 | - [rhai](https://github.com/rhaiscript/rhai) The Rust crate doing most of the dirty work. 150 | - [RustlerPrecompiled](https://github.com/philss/rustler_precompiled) Use precompiled NIFs from trusted sources in your Elixir code. 151 | - [NimbleLZ4](https://github.com/whatyouhide/nimble_lz4) Major inspiration for the RustlerPrecompiled GitHub actions workflow and general setup. 152 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, 3 | "castore": {:hex, :castore, "1.0.11", "4bbd584741601eb658007339ea730b082cc61f3554cf2e8f39bf693a11b49073", [:mix], [], "hexpm", "e03990b4db988df56262852f20de0f659871c35154691427a5047f4967a16a62"}, 4 | "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, 5 | "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, 6 | "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, 7 | "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, 8 | "ex_doc": {:hex, :ex_doc, "0.38.1", "bae0a0bd5b5925b1caef4987e3470902d072d03347114ffe03a55dbe206dd4c2", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "754636236d191b895e1e4de2ebb504c057fe1995fdfdd92e9d75c4b05633008b"}, 9 | "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, 10 | "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, 11 | "hpax": {:hex, :hpax, "1.0.2", "762df951b0c399ff67cc57c3995ec3cf46d696e41f0bba17da0518d94acd4aac", [:mix], [], "hexpm", "2f09b4c1074e0abd846747329eaa26d535be0eb3d189fa69d812bfb8bfefd32f"}, 12 | "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, 13 | "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, 14 | "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, 15 | "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, 16 | "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, 17 | "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, 18 | "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, 19 | "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, 20 | "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, 21 | "req": {:hex, :req, "0.5.8", "50d8d65279d6e343a5e46980ac2a70e97136182950833a1968b371e753f6a662", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d7fc5898a566477e174f26887821a3c5082b243885520ee4b45555f5d53f40ef"}, 22 | "rustler": {:hex, :rustler, "0.36.1", "2d4b1ff57ea2789a44756a40dbb5fbb73c6ee0a13d031dcba96d0a5542598a6a", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.7", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "f3fba4ad272970e0d1bc62972fc4a99809651e54a125c5242de9bad4574b2d02"}, 23 | "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.2", "5f25cbe220a8fac3e7ad62e6f950fcdca5a5a5f8501835d2823e8c74bf4268d5", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "63d1bd5f8e23096d1ff851839923162096364bac8656a4a3c00d1fff8e83ee0a"}, 24 | "stream_data": {:hex, :stream_data, "1.2.0", "58dd3f9e88afe27dc38bef26fce0c84a9e7a96772b2925c7b32cd2435697a52b", [:mix], [], "hexpm", "eb5c546ee3466920314643edf68943a5b14b32d1da9fe01698dc92b73f89a9ed"}, 25 | "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, 26 | "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, 27 | } 28 | -------------------------------------------------------------------------------- /lib/rhai/scope.ex: -------------------------------------------------------------------------------- 1 | defmodule Rhai.Scope do 2 | @moduledoc """ 3 | Type containing information about the current scope. Useful for keeping state between Engine evaluation runs. 4 | Scope implements the [https://hexdocs.pm/elixir/1.12/Enumerable.html](Enumerable) protocol. 5 | """ 6 | 7 | defstruct [ 8 | # The actual NIF Resource. 9 | resource: nil, 10 | # Normally the compiler will happily do stuff like inlining the 11 | # resource in attributes. This will convert the resource into an 12 | # empty binary with no warning. This will make that harder to 13 | # accidentaly do. 14 | # It also serves as a handy way to tell file handles apart. 15 | reference: nil 16 | ] 17 | 18 | @type t :: %__MODULE__{} 19 | 20 | @doc """ 21 | Create a new Scope 22 | """ 23 | @spec new :: t() 24 | def new do 25 | wrap_resource(Rhai.Native.scope_new()) 26 | end 27 | 28 | @doc """ 29 | Create a new Scope with a particular capacity. 30 | """ 31 | @spec with_capacity(non_neg_integer()) :: t() 32 | def with_capacity(capacity) do 33 | capacity 34 | |> Rhai.Native.scope_with_capacity() 35 | |> wrap_resource() 36 | end 37 | 38 | @doc """ 39 | Add (push) a new entry to the Scope. 40 | """ 41 | @spec push(t(), String.t(), Rhai.Any.t()) :: t() 42 | def push(%__MODULE__{resource: resource} = scope, name, value) do 43 | Rhai.Native.scope_push_dynamic(resource, name, value) 44 | 45 | scope 46 | end 47 | 48 | @doc """ 49 | Add (push) a new constant to the Scope. 50 | 51 | Constants are immutable and cannot be assigned to. Their values never change. 52 | Constants propagation is a technique used to optimize an AST. 53 | """ 54 | @spec push_constant(t(), String.t(), Rhai.Any.t()) :: t() 55 | def push_constant(%__MODULE__{resource: resource} = scope, name, value) do 56 | Rhai.Native.scope_push_constant_dynamic(resource, name, value) 57 | 58 | scope 59 | end 60 | 61 | @doc """ 62 | Does the Scope contain the entry? 63 | """ 64 | @spec contains?(t(), String.t()) :: bool() 65 | def contains?(%__MODULE__{resource: resource}, name) do 66 | Rhai.Native.scope_contains(resource, name) 67 | end 68 | 69 | @doc """ 70 | Check if the named entry in the Scope is constant. 71 | Search starts backwards from the last, stopping at the first entry matching the specified name. 72 | Returns nil if no entry matching the specified name is found. 73 | """ 74 | @spec constant?(t(), String.t()) :: nil | bool() 75 | def constant?(%__MODULE__{resource: resource}, name) do 76 | Rhai.Native.scope_is_constant(resource, name) 77 | end 78 | 79 | @doc """ 80 | Get the value of an entry in the Scope, starting from the last. 81 | """ 82 | @spec get_value(t(), String.t()) :: nil | Rhai.Any.t() 83 | def get_value(%__MODULE__{resource: resource}, name) do 84 | Rhai.Native.scope_get_value(resource, name) 85 | end 86 | 87 | @doc """ 88 | Empty the Scope. 89 | """ 90 | @spec clear(t()) :: t() 91 | def clear(%__MODULE__{resource: resource} = scope) do 92 | Rhai.Native.scope_clear(resource) 93 | 94 | scope 95 | end 96 | 97 | @doc """ 98 | Clone the Scope, keeping only the last instances of each variable name. Shadowed variables are omitted in the copy. 99 | """ 100 | @spec clone_visible(t()) :: t() 101 | def clone_visible(%__MODULE__{resource: resource}) do 102 | resource 103 | |> Rhai.Native.scope_clone_visible() 104 | |> wrap_resource() 105 | end 106 | 107 | @doc """ 108 | Returns true if this Scope contains no variables. 109 | """ 110 | @spec empty?(t()) :: bool() 111 | def empty?(%__MODULE__{resource: resource}) do 112 | Rhai.Native.scope_is_empty(resource) 113 | end 114 | 115 | @doc """ 116 | Get the number of entries inside the Scope. 117 | """ 118 | @spec len(t()) :: non_neg_integer() 119 | def len(%__MODULE__{resource: resource}) do 120 | Rhai.Native.scope_len(resource) 121 | end 122 | 123 | @doc """ 124 | Remove the last entry in the Scope by the specified name and return its value. 125 | 126 | If the entry by the specified name is not found, None is returned. 127 | """ 128 | @spec remove(t(), String.t()) :: nil | Rhai.Any.t() 129 | def remove(%__MODULE__{resource: resource}, name) do 130 | Rhai.Native.scope_remove(resource, name) 131 | end 132 | 133 | @doc """ 134 | Truncate (rewind) the Scope to a previous size. 135 | """ 136 | @spec rewind(t(), non_neg_integer()) :: t() 137 | def rewind(%__MODULE__{resource: resource} = scope, size) do 138 | Rhai.Native.scope_rewind(resource, size) 139 | 140 | scope 141 | end 142 | 143 | @doc """ 144 | Remove the last entry from the Scope. 145 | 146 | Returns an error if the Scope is empty. 147 | """ 148 | @spec pop(t()) :: {:ok, t()} | {:error, {:scope_is_empty, String.t()}} 149 | def pop(%__MODULE__{resource: resource} = scope) do 150 | case Rhai.Native.scope_pop(resource) do 151 | {:ok, _} -> 152 | {:ok, scope} 153 | 154 | error -> 155 | error 156 | end 157 | end 158 | 159 | @doc """ 160 | Remove the last entry from the Scope. 161 | 162 | Raises if the Scope is empty. 163 | """ 164 | @spec pop!(t()) :: t() 165 | def pop!(%__MODULE__{} = scope) do 166 | case pop(scope) do 167 | {:ok, _} -> 168 | scope 169 | 170 | {:error, {:scope_is_empty, message}} -> 171 | raise message 172 | end 173 | end 174 | 175 | @doc """ 176 | Update the value of the named entry in the Scope. 177 | 178 | Search starts backwards from the last, and only the first entry matching the specified name is updated. 179 | If no entry matching the specified name is found, a new one is added. 180 | 181 | Returns an error when trying to update the value of a constant. 182 | """ 183 | @spec set_value(t(), String.t(), Rhai.Any.t()) :: 184 | {:ok, t()} | {:error, {:cannot_update_value_of_constant, String.t()}} 185 | def set_value(%__MODULE__{resource: resource} = scope, name, value) do 186 | case Rhai.Native.scope_set_value(resource, name, value) do 187 | {:ok, _} -> 188 | {:ok, scope} 189 | 190 | error -> 191 | error 192 | end 193 | end 194 | 195 | @doc """ 196 | Update the value of the named entry in the Scope. 197 | 198 | Search starts backwards from the last, and only the first entry matching the specified name is updated. 199 | If no entry matching the specified name is found, a new one is added. 200 | 201 | Raises when trying to update the value of a constant. 202 | """ 203 | @spec set_value!(t(), String.t(), Rhai.Any.t()) :: t() 204 | def set_value!(%__MODULE__{} = scope, name, value) do 205 | case set_value(scope, name, value) do 206 | {:ok, _} -> 207 | scope 208 | 209 | {:error, {:cannot_update_value_of_constant, message}} -> 210 | raise message 211 | end 212 | end 213 | 214 | @doc """ 215 | Update the value of the named entry in the Scope if it already exists and is not constant. 216 | Push a new entry with the value into the Scope if the name doesn’t exist or if the existing entry is constant. 217 | 218 | Search starts backwards from the last, and only the first entry matching the specified name is updated. 219 | """ 220 | @spec set_or_push(t(), String.t(), Rhai.Any.t()) :: t() 221 | def set_or_push(%__MODULE__{resource: resource} = scope, name, value) do 222 | Rhai.Native.scope_set_or_push(resource, name, value) 223 | 224 | scope 225 | end 226 | 227 | @doc false 228 | def wrap_resource(resource) do 229 | %__MODULE__{ 230 | resource: resource, 231 | reference: make_ref() 232 | } 233 | end 234 | 235 | defimpl Enumerable do 236 | def count(scope) do 237 | {:ok, Rhai.Scope.len(scope)} 238 | end 239 | 240 | def member?(scope, {name, value}) do 241 | {:ok, value == Rhai.Scope.get_value(scope, name)} 242 | end 243 | 244 | def reduce(%Rhai.Scope{resource: resource}, acc, fun) do 245 | resource 246 | |> Rhai.Native.scope_iter_collect() 247 | |> Enumerable.List.reduce(acc, fun) 248 | end 249 | 250 | # Since it returns `{:error, __MODULE__}`, a default implementation will be used. 251 | def slice(_), do: {:error, __MODULE__} 252 | end 253 | end 254 | -------------------------------------------------------------------------------- /.credo.exs: -------------------------------------------------------------------------------- 1 | # This file contains the configuration for Credo and you are probably reading 2 | # this after creating it with `mix credo.gen.config`. 3 | # 4 | # If you find anything wrong or unclear in this file, please report an 5 | # issue on GitHub: https://github.com/rrrene/credo/issues 6 | # 7 | %{ 8 | # 9 | # You can have as many configs as you like in the `configs:` field. 10 | configs: [ 11 | %{ 12 | # 13 | # Run any config using `mix credo -C `. If no config name is given 14 | # "default" is used. 15 | # 16 | name: "default", 17 | # 18 | # These are the files included in the analysis: 19 | files: %{ 20 | # 21 | # You can give explicit globs or simply directories. 22 | # In the latter case `**/*.{ex,exs}` will be used. 23 | # 24 | included: [ 25 | "lib/", 26 | "src/", 27 | "test/", 28 | "web/", 29 | "apps/*/lib/", 30 | "apps/*/src/", 31 | "apps/*/test/", 32 | "apps/*/web/" 33 | ], 34 | excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"] 35 | }, 36 | # 37 | # Load and configure plugins here: 38 | # 39 | plugins: [], 40 | # 41 | # If you create your own checks, you must specify the source files for 42 | # them here, so they can be loaded by Credo before running the analysis. 43 | # 44 | requires: [], 45 | # 46 | # If you want to enforce a style guide and need a more traditional linting 47 | # experience, you can change `strict` to `true` below: 48 | # 49 | strict: false, 50 | # 51 | # To modify the timeout for parsing files, change this value: 52 | # 53 | parse_timeout: 5000, 54 | # 55 | # If you want to use uncolored output by default, you can change `color` 56 | # to `false` below: 57 | # 58 | color: true, 59 | # 60 | # You can customize the parameters of any check by adding a second element 61 | # to the tuple. 62 | # 63 | # To disable a check put `false` as second element: 64 | # 65 | # {Credo.Check.Design.DuplicatedCode, false} 66 | # 67 | checks: %{ 68 | enabled: [ 69 | # 70 | ## Consistency Checks 71 | # 72 | {Credo.Check.Consistency.ExceptionNames, []}, 73 | {Credo.Check.Consistency.LineEndings, []}, 74 | {Credo.Check.Consistency.ParameterPatternMatching, []}, 75 | {Credo.Check.Consistency.SpaceAroundOperators, []}, 76 | {Credo.Check.Consistency.SpaceInParentheses, []}, 77 | {Credo.Check.Consistency.TabsOrSpaces, []}, 78 | 79 | # 80 | ## Design Checks 81 | # 82 | # You can customize the priority of any check 83 | # Priority values are: `low, normal, high, higher` 84 | # 85 | {Credo.Check.Design.AliasUsage, 86 | [priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]}, 87 | # You can also customize the exit_status of each check. 88 | # If you don't want TODO comments to cause `mix credo` to fail, just 89 | # set this value to 0 (zero). 90 | # 91 | {Credo.Check.Design.TagTODO, [exit_status: 0]}, 92 | {Credo.Check.Design.TagFIXME, [exit_status: 0]}, 93 | 94 | # 95 | ## Readability Checks 96 | # 97 | {Credo.Check.Readability.AliasOrder, []}, 98 | {Credo.Check.Readability.FunctionNames, []}, 99 | {Credo.Check.Readability.LargeNumbers, []}, 100 | {Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]}, 101 | {Credo.Check.Readability.ModuleAttributeNames, []}, 102 | {Credo.Check.Readability.ModuleDoc, []}, 103 | {Credo.Check.Readability.ModuleNames, []}, 104 | {Credo.Check.Readability.ParenthesesInCondition, []}, 105 | {Credo.Check.Readability.ParenthesesOnZeroArityDefs, []}, 106 | {Credo.Check.Readability.PipeIntoAnonymousFunctions, []}, 107 | {Credo.Check.Readability.PredicateFunctionNames, []}, 108 | {Credo.Check.Readability.PreferImplicitTry, []}, 109 | {Credo.Check.Readability.RedundantBlankLines, []}, 110 | {Credo.Check.Readability.Semicolons, []}, 111 | {Credo.Check.Readability.SinglePipe, [allow_0_arity_functions: true]}, 112 | {Credo.Check.Readability.SpaceAfterCommas, []}, 113 | {Credo.Check.Readability.StringSigils, []}, 114 | {Credo.Check.Readability.TrailingBlankLine, []}, 115 | {Credo.Check.Readability.TrailingWhiteSpace, []}, 116 | {Credo.Check.Readability.UnnecessaryAliasExpansion, []}, 117 | {Credo.Check.Readability.VariableNames, []}, 118 | {Credo.Check.Readability.WithSingleClause, []}, 119 | 120 | # 121 | ## Refactoring Opportunities 122 | # 123 | {Credo.Check.Refactor.Apply, []}, 124 | {Credo.Check.Refactor.CondStatements, []}, 125 | {Credo.Check.Refactor.CyclomaticComplexity, []}, 126 | {Credo.Check.Refactor.FunctionArity, []}, 127 | {Credo.Check.Refactor.LongQuoteBlocks, []}, 128 | {Credo.Check.Refactor.MatchInCondition, []}, 129 | {Credo.Check.Refactor.MapJoin, []}, 130 | {Credo.Check.Refactor.NegatedConditionsInUnless, []}, 131 | {Credo.Check.Refactor.NegatedConditionsWithElse, []}, 132 | {Credo.Check.Refactor.Nesting, []}, 133 | {Credo.Check.Refactor.UnlessWithElse, []}, 134 | {Credo.Check.Refactor.WithClauses, []}, 135 | {Credo.Check.Refactor.FilterFilter, []}, 136 | {Credo.Check.Refactor.RejectReject, []}, 137 | {Credo.Check.Refactor.RedundantWithClauseResult, []}, 138 | 139 | # 140 | ## Warnings 141 | # 142 | {Credo.Check.Warning.ApplicationConfigInModuleAttribute, []}, 143 | {Credo.Check.Warning.BoolOperationOnSameValues, []}, 144 | {Credo.Check.Warning.ExpensiveEmptyEnumCheck, []}, 145 | {Credo.Check.Warning.IExPry, []}, 146 | {Credo.Check.Warning.IoInspect, []}, 147 | {Credo.Check.Warning.OperationOnSameValues, []}, 148 | {Credo.Check.Warning.OperationWithConstantResult, []}, 149 | {Credo.Check.Warning.RaiseInsideRescue, []}, 150 | {Credo.Check.Warning.SpecWithStruct, []}, 151 | {Credo.Check.Warning.WrongTestFileExtension, []}, 152 | {Credo.Check.Warning.UnusedEnumOperation, []}, 153 | {Credo.Check.Warning.UnusedFileOperation, []}, 154 | {Credo.Check.Warning.UnusedKeywordOperation, []}, 155 | {Credo.Check.Warning.UnusedListOperation, []}, 156 | {Credo.Check.Warning.UnusedPathOperation, []}, 157 | {Credo.Check.Warning.UnusedRegexOperation, []}, 158 | {Credo.Check.Warning.UnusedStringOperation, []}, 159 | {Credo.Check.Warning.UnusedTupleOperation, []}, 160 | {Credo.Check.Warning.UnsafeExec, []}, 161 | 162 | # Controversial checks 163 | {Credo.Check.Readability.StrictModuleLayout, []} 164 | ], 165 | disabled: [ 166 | # 167 | # Checks scheduled for next check update (opt-in for now, just replace `false` with `[]`) 168 | 169 | # 170 | # Controversial and experimental checks (opt-in, just move the check to `:enabled` 171 | # and be sure to use `mix credo --strict` to see low priority checks) 172 | # 173 | {Credo.Check.Consistency.MultiAliasImportRequireUse, []}, 174 | {Credo.Check.Consistency.UnusedVariableNames, []}, 175 | {Credo.Check.Design.DuplicatedCode, []}, 176 | {Credo.Check.Design.SkipTestWithoutComment, []}, 177 | {Credo.Check.Readability.AliasAs, []}, 178 | {Credo.Check.Readability.BlockPipe, []}, 179 | {Credo.Check.Readability.ImplTrue, []}, 180 | {Credo.Check.Readability.MultiAlias, []}, 181 | {Credo.Check.Readability.NestedFunctionCalls, []}, 182 | {Credo.Check.Readability.SeparateAliasRequire, []}, 183 | {Credo.Check.Readability.SingleFunctionToBlockPipe, []}, 184 | {Credo.Check.Readability.Specs, []}, 185 | {Credo.Check.Readability.WithCustomTaggedTuple, []}, 186 | {Credo.Check.Refactor.ABCSize, []}, 187 | {Credo.Check.Refactor.AppendSingleItem, []}, 188 | {Credo.Check.Refactor.DoubleBooleanNegation, []}, 189 | {Credo.Check.Refactor.FilterReject, []}, 190 | {Credo.Check.Refactor.IoPuts, []}, 191 | {Credo.Check.Refactor.MapMap, []}, 192 | {Credo.Check.Refactor.ModuleDependencies, []}, 193 | {Credo.Check.Refactor.NegatedIsNil, []}, 194 | {Credo.Check.Refactor.PipeChainStart, []}, 195 | {Credo.Check.Refactor.RejectFilter, []}, 196 | {Credo.Check.Refactor.VariableRebinding, []}, 197 | {Credo.Check.Warning.LazyLogging, []}, 198 | {Credo.Check.Warning.LeakyEnvironment, []}, 199 | {Credo.Check.Warning.MapGetUnsafePass, []}, 200 | {Credo.Check.Warning.MixEnv, []}, 201 | {Credo.Check.Warning.UnsafeToAtom, []} 202 | 203 | # {Credo.Check.Refactor.MapInto, []}, 204 | 205 | # 206 | # Custom checks can be created using `mix credo.gen.check`. 207 | # 208 | ] 209 | } 210 | } 211 | ] 212 | } 213 | -------------------------------------------------------------------------------- /test/rhai/scope_test.exs: -------------------------------------------------------------------------------- 1 | defmodule ScopeTest do 2 | use ExUnit.Case 3 | 4 | alias Rhai.Scope 5 | 6 | describe "new/0" do 7 | test "should create a new scope" do 8 | assert %Scope{} = Scope.new() 9 | end 10 | end 11 | 12 | describe "with_capacity/1" do 13 | test "should create a new scope with a particular capacity" do 14 | assert %Scope{} = Scope.with_capacity(10) 15 | end 16 | end 17 | 18 | describe "push/3" do 19 | test "should push a dynamic variable" do 20 | scope = Scope.new() |> Scope.push("a", 1) 21 | 22 | assert 1 = Scope.get_value(scope, "a") 23 | assert Scope.contains?(scope, "a") 24 | refute Scope.constant?(scope, "a") 25 | end 26 | end 27 | 28 | describe "push_constant/3" do 29 | test "should push a dynamic variable" do 30 | scope = Scope.new() |> Scope.push_constant("a", 1) 31 | 32 | assert 1 = Scope.get_value(scope, "a") 33 | assert Scope.contains?(scope, "a") 34 | assert Scope.constant?(scope, "a") 35 | end 36 | end 37 | 38 | describe "get_value/2" do 39 | test "should return the value of the variable" do 40 | scope = Scope.new() |> Scope.push("a", 1) 41 | 42 | assert 1 == Scope.get_value(scope, "a") 43 | end 44 | 45 | test "should return nil if the variable is not found" do 46 | scope = Scope.new() 47 | 48 | assert nil == Scope.get_value(scope, "a") 49 | end 50 | end 51 | 52 | describe "contains?/2" do 53 | test "should return true if the variable is found" do 54 | scope = Scope.new() |> Scope.push("a", 1) 55 | 56 | assert Scope.contains?(scope, "a") 57 | end 58 | 59 | test "should return false if the variable is not found" do 60 | scope = Scope.new() 61 | 62 | refute Scope.contains?(scope, "a") 63 | end 64 | end 65 | 66 | describe "constant?/2" do 67 | test "should return true if the variable is a constant" do 68 | scope = Scope.new() |> Scope.push_constant("a", 1) 69 | 70 | assert Scope.constant?(scope, "a") 71 | end 72 | 73 | test "should return nil if the variable is not found" do 74 | scope = Scope.new() 75 | 76 | assert nil == Scope.constant?(scope, "a") 77 | end 78 | end 79 | 80 | describe "clear/1" do 81 | test "should clear the scope" do 82 | scope = Scope.new() |> Scope.push("a", 1) |> Scope.clear() 83 | 84 | refute Scope.contains?(scope, "a") 85 | end 86 | end 87 | 88 | describe "clone_visible/1" do 89 | test "should clone the scope" do 90 | scope = 91 | Scope.new() 92 | |> Scope.push("a", 1) 93 | |> Scope.push("a", 2) 94 | |> Scope.clone_visible() 95 | 96 | assert 2 == Scope.get_value(scope, "a") 97 | end 98 | end 99 | 100 | describe "empty?/1" do 101 | test "should return true if the scope is empty" do 102 | scope = Scope.new() 103 | 104 | assert Scope.empty?(scope) 105 | end 106 | 107 | test "should return false if the scope is not empty" do 108 | scope = Scope.new() |> Scope.push("a", 1) 109 | 110 | refute Scope.empty?(scope) 111 | end 112 | end 113 | 114 | describe "len/1" do 115 | test "should return the number of entries inside the scope" do 116 | scope = Scope.new() 117 | 118 | assert 0 == Scope.len(scope) 119 | 120 | scope = 121 | scope 122 | |> Scope.push("a", 1) 123 | |> Scope.push("b", 2) 124 | 125 | assert 2 == Scope.len(scope) 126 | end 127 | end 128 | 129 | describe "remove/2" do 130 | test "should remove and return the variable from the scope" do 131 | scope = Scope.new() 132 | 133 | assert 1 == scope |> Scope.push("a", 1) |> Scope.remove("a") 134 | refute Scope.contains?(scope, "a") 135 | end 136 | 137 | test "should do nothing and return nil if the variable is not found" do 138 | scope = Scope.new() 139 | 140 | assert nil == Scope.remove(scope, "a") 141 | refute Scope.contains?(scope, "a") 142 | end 143 | end 144 | 145 | describe "rewind/2" do 146 | test "should rewind the scope to a previous size" do 147 | scope = 148 | Scope.new() 149 | |> Scope.push("a", 1) 150 | |> Scope.push("b", 2) 151 | |> Scope.push("c", 3) 152 | |> Scope.rewind(2) 153 | 154 | assert 2 == Scope.len(scope) 155 | assert 1 == Scope.get_value(scope, "a") 156 | assert 2 == Scope.get_value(scope, "b") 157 | refute Scope.contains?(scope, "c") 158 | 159 | scope = Scope.rewind(scope, 0) 160 | 161 | assert Scope.empty?(scope) 162 | end 163 | end 164 | 165 | describe "pop/1" do 166 | test "should pop remove the last entry from the Scope" do 167 | scope = 168 | Scope.new() 169 | |> Scope.push("a", 1) 170 | |> Scope.push("b", 2) 171 | |> Scope.push("c", 3) 172 | 173 | assert {:ok, scope} = Scope.pop(scope) 174 | assert 2 == Scope.len(scope) 175 | refute Scope.contains?(scope, "c") 176 | end 177 | 178 | test "should return an error if the scope is empty" do 179 | assert {:error, {:scope_is_empty, _}} = Scope.new() |> Scope.pop() 180 | end 181 | end 182 | 183 | describe "pop!/1" do 184 | test "should pop remove the last entry from the Scope" do 185 | scope = 186 | Scope.new() 187 | |> Scope.push("a", 1) 188 | |> Scope.push("b", 2) 189 | |> Scope.push("c", 3) 190 | 191 | scope = Scope.pop!(scope) 192 | 193 | assert 2 == Scope.len(scope) 194 | refute Scope.contains?(scope, "c") 195 | end 196 | 197 | test "should raise if the scope is empty" do 198 | assert_raise RuntimeError, fn -> 199 | Scope.new() |> Scope.pop!() 200 | end 201 | end 202 | end 203 | 204 | describe "set_value/1" do 205 | test "should update the value of the named entry in the Scope" do 206 | assert {:ok, scope} = 207 | Scope.new() 208 | |> Scope.push("a", 1) 209 | |> Scope.set_value("a", 2) 210 | 211 | assert 2 == Scope.get_value(scope, "a") 212 | end 213 | 214 | test "should add a new entry if no entry matching the specified name is found" do 215 | assert {:ok, scope} = Scope.new() |> Scope.set_value("a", 1) 216 | 217 | assert 1 == Scope.get_value(scope, "a") 218 | end 219 | 220 | test "should return an error when trying to update the value of a constant" do 221 | assert {:error, {:cannot_update_value_of_constant, _}} = 222 | Scope.new() 223 | |> Scope.push_constant("a", 1) 224 | |> Scope.set_value("a", 2) 225 | end 226 | end 227 | 228 | describe "set_value!/1" do 229 | test "should update the value of the named entry in the Scope" do 230 | scope = 231 | Scope.new() 232 | |> Scope.push("a", 1) 233 | |> Scope.set_value!("a", 2) 234 | 235 | assert 2 == Scope.get_value(scope, "a") 236 | end 237 | 238 | test "should add a new entry if no entry matching the specified name is found" do 239 | scope = Scope.new() |> Scope.set_value!("a", 1) 240 | 241 | assert 1 == Scope.get_value(scope, "a") 242 | end 243 | 244 | test "should raise when trying to update the value of a constant" do 245 | assert_raise RuntimeError, fn -> 246 | Scope.new() 247 | |> Scope.push_constant("a", 1) 248 | |> Scope.set_value!("a", 2) 249 | end 250 | end 251 | end 252 | 253 | describe "set_or_push/3" do 254 | test "should update the value of the named entry in the Scope" do 255 | scope = 256 | Scope.new() 257 | |> Scope.push("a", 1) 258 | |> Scope.set_or_push("a", 2) 259 | 260 | assert 2 == Scope.get_value(scope, "a") 261 | end 262 | 263 | test "should push a new entry into the Scope if the entry does not exist" do 264 | assert 1 == 265 | Scope.new() 266 | |> Scope.set_or_push("a", 1) 267 | |> Scope.get_value("a") 268 | end 269 | 270 | test "should push a new entry into the Scope if the existing entry is constant" do 271 | scope = 272 | Scope.new() 273 | |> Scope.push_constant("a", 1) 274 | |> Scope.set_or_push("a", 2) 275 | 276 | assert 2 == Scope.get_value(scope, "a") 277 | end 278 | end 279 | 280 | describe "Enumerable" do 281 | setup do 282 | scope = 283 | Scope.new() 284 | |> Scope.push("a", 1) 285 | |> Scope.push("b", 2) 286 | |> Scope.push("c", 3) 287 | 288 | {:ok, %{scope: scope}} 289 | end 290 | 291 | test "Enum.to_list/1", %{scope: scope} do 292 | assert [ 293 | {"a", 1}, 294 | {"b", 2}, 295 | {"c", 3} 296 | ] = Enum.to_list(scope) 297 | end 298 | 299 | test "Enum.count/1", %{scope: scope} do 300 | assert 3 = Enum.count(scope) 301 | end 302 | 303 | test "left in right", %{scope: scope} do 304 | assert {"a", 1} in scope 305 | end 306 | 307 | test "Enum.map/2", %{scope: scope} do 308 | assert [ 309 | {"a", 2}, 310 | {"b", 4}, 311 | {"c", 6} 312 | ] = Enum.map(scope, fn {k, v} -> {k, v * 2} end) 313 | end 314 | 315 | test "Enum.slice/2", %{scope: scope} do 316 | assert [ 317 | {"a", 1}, 318 | {"b", 2} 319 | ] = Enum.slice(scope, 0..1) 320 | end 321 | end 322 | end 323 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /native/test_dylib_module/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.8.11" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 10 | dependencies = [ 11 | "cfg-if", 12 | "const-random", 13 | "getrandom", 14 | "once_cell", 15 | "version_check", 16 | "zerocopy", 17 | ] 18 | 19 | [[package]] 20 | name = "autocfg" 21 | version = "1.4.0" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 24 | 25 | [[package]] 26 | name = "bitflags" 27 | version = "2.8.0" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 30 | 31 | [[package]] 32 | name = "cfg-if" 33 | version = "1.0.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 36 | 37 | [[package]] 38 | name = "const-random" 39 | version = "0.1.18" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" 42 | dependencies = [ 43 | "const-random-macro", 44 | ] 45 | 46 | [[package]] 47 | name = "const-random-macro" 48 | version = "0.1.16" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" 51 | dependencies = [ 52 | "getrandom", 53 | "once_cell", 54 | "tiny-keccak", 55 | ] 56 | 57 | [[package]] 58 | name = "crunchy" 59 | version = "0.2.3" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" 62 | 63 | [[package]] 64 | name = "getrandom" 65 | version = "0.2.15" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 68 | dependencies = [ 69 | "cfg-if", 70 | "libc", 71 | "wasi", 72 | ] 73 | 74 | [[package]] 75 | name = "heck" 76 | version = "0.5.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 79 | 80 | [[package]] 81 | name = "instant" 82 | version = "0.1.13" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 85 | dependencies = [ 86 | "cfg-if", 87 | ] 88 | 89 | [[package]] 90 | name = "inventory" 91 | version = "0.3.19" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "54b12ebb6799019b044deaf431eadfe23245b259bba5a2c0796acec3943a3cdb" 94 | dependencies = [ 95 | "rustversion", 96 | ] 97 | 98 | [[package]] 99 | name = "libc" 100 | version = "0.2.170" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" 103 | 104 | [[package]] 105 | name = "libloading" 106 | version = "0.8.6" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" 109 | dependencies = [ 110 | "cfg-if", 111 | "windows-targets", 112 | ] 113 | 114 | [[package]] 115 | name = "no-std-compat" 116 | version = "0.4.1" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" 119 | dependencies = [ 120 | "spin", 121 | ] 122 | 123 | [[package]] 124 | name = "num-traits" 125 | version = "0.2.19" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 128 | dependencies = [ 129 | "autocfg", 130 | ] 131 | 132 | [[package]] 133 | name = "once_cell" 134 | version = "1.20.3" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" 137 | dependencies = [ 138 | "portable-atomic", 139 | ] 140 | 141 | [[package]] 142 | name = "portable-atomic" 143 | version = "1.11.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" 146 | 147 | [[package]] 148 | name = "proc-macro2" 149 | version = "1.0.93" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 152 | dependencies = [ 153 | "unicode-ident", 154 | ] 155 | 156 | [[package]] 157 | name = "quote" 158 | version = "1.0.38" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 161 | dependencies = [ 162 | "proc-macro2", 163 | ] 164 | 165 | [[package]] 166 | name = "regex-lite" 167 | version = "0.1.6" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" 170 | 171 | [[package]] 172 | name = "rhai" 173 | version = "1.21.0" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "ce4d759a4729a655ddfdbb3ff6e77fb9eadd902dae12319455557796e435d2a6" 176 | dependencies = [ 177 | "ahash", 178 | "bitflags", 179 | "instant", 180 | "no-std-compat", 181 | "num-traits", 182 | "once_cell", 183 | "rhai_codegen", 184 | "smallvec", 185 | "smartstring", 186 | "thin-vec", 187 | ] 188 | 189 | [[package]] 190 | name = "rhai-dylib" 191 | version = "0.6.0" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "1ba94b374f7c1773444814b2b99275206c1dcce454e39e38dbaa9c6e7d6aeba5" 194 | dependencies = [ 195 | "libloading", 196 | "rhai", 197 | ] 198 | 199 | [[package]] 200 | name = "rhai_codegen" 201 | version = "2.2.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" 204 | dependencies = [ 205 | "proc-macro2", 206 | "quote", 207 | "syn", 208 | ] 209 | 210 | [[package]] 211 | name = "rustler" 212 | version = "0.36.1" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "f04a7b61bf2db5495d6c0d2eb4b3f0f366864d47f2482834656e25d1b25fe290" 215 | dependencies = [ 216 | "inventory", 217 | "libloading", 218 | "regex-lite", 219 | "rustler_codegen", 220 | ] 221 | 222 | [[package]] 223 | name = "rustler_codegen" 224 | version = "0.36.1" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "bf9365a04e3a3a4d3136953d97c67fd0a9c036d36197917961551c2cc1ecb385" 227 | dependencies = [ 228 | "heck", 229 | "inventory", 230 | "proc-macro2", 231 | "quote", 232 | "syn", 233 | ] 234 | 235 | [[package]] 236 | name = "rustversion" 237 | version = "1.0.19" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 240 | 241 | [[package]] 242 | name = "smallvec" 243 | version = "1.14.0" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 246 | 247 | [[package]] 248 | name = "smartstring" 249 | version = "1.0.1" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" 252 | dependencies = [ 253 | "autocfg", 254 | "static_assertions", 255 | "version_check", 256 | ] 257 | 258 | [[package]] 259 | name = "spin" 260 | version = "0.5.2" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 263 | 264 | [[package]] 265 | name = "static_assertions" 266 | version = "1.1.0" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 269 | 270 | [[package]] 271 | name = "syn" 272 | version = "2.0.98" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" 275 | dependencies = [ 276 | "proc-macro2", 277 | "quote", 278 | "unicode-ident", 279 | ] 280 | 281 | [[package]] 282 | name = "test_dylib_module" 283 | version = "0.1.0" 284 | dependencies = [ 285 | "rhai-dylib", 286 | "rustler", 287 | ] 288 | 289 | [[package]] 290 | name = "thin-vec" 291 | version = "0.2.13" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" 294 | 295 | [[package]] 296 | name = "tiny-keccak" 297 | version = "2.0.2" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 300 | dependencies = [ 301 | "crunchy", 302 | ] 303 | 304 | [[package]] 305 | name = "unicode-ident" 306 | version = "1.0.17" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" 309 | 310 | [[package]] 311 | name = "version_check" 312 | version = "0.9.5" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 315 | 316 | [[package]] 317 | name = "wasi" 318 | version = "0.11.0+wasi-snapshot-preview1" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 321 | 322 | [[package]] 323 | name = "windows-targets" 324 | version = "0.52.6" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 327 | dependencies = [ 328 | "windows_aarch64_gnullvm", 329 | "windows_aarch64_msvc", 330 | "windows_i686_gnu", 331 | "windows_i686_gnullvm", 332 | "windows_i686_msvc", 333 | "windows_x86_64_gnu", 334 | "windows_x86_64_gnullvm", 335 | "windows_x86_64_msvc", 336 | ] 337 | 338 | [[package]] 339 | name = "windows_aarch64_gnullvm" 340 | version = "0.52.6" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 343 | 344 | [[package]] 345 | name = "windows_aarch64_msvc" 346 | version = "0.52.6" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 349 | 350 | [[package]] 351 | name = "windows_i686_gnu" 352 | version = "0.52.6" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 355 | 356 | [[package]] 357 | name = "windows_i686_gnullvm" 358 | version = "0.52.6" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 361 | 362 | [[package]] 363 | name = "windows_i686_msvc" 364 | version = "0.52.6" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 367 | 368 | [[package]] 369 | name = "windows_x86_64_gnu" 370 | version = "0.52.6" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 373 | 374 | [[package]] 375 | name = "windows_x86_64_gnullvm" 376 | version = "0.52.6" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 379 | 380 | [[package]] 381 | name = "windows_x86_64_msvc" 382 | version = "0.52.6" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 385 | 386 | [[package]] 387 | name = "zerocopy" 388 | version = "0.7.35" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 391 | dependencies = [ 392 | "zerocopy-derive", 393 | ] 394 | 395 | [[package]] 396 | name = "zerocopy-derive" 397 | version = "0.7.35" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 400 | dependencies = [ 401 | "proc-macro2", 402 | "quote", 403 | "syn", 404 | ] 405 | -------------------------------------------------------------------------------- /native/rhai_rustler/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.8.11" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 10 | dependencies = [ 11 | "cfg-if", 12 | "const-random", 13 | "getrandom", 14 | "once_cell", 15 | "version_check", 16 | "zerocopy", 17 | ] 18 | 19 | [[package]] 20 | name = "autocfg" 21 | version = "1.4.0" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 24 | 25 | [[package]] 26 | name = "bitflags" 27 | version = "2.8.0" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 30 | 31 | [[package]] 32 | name = "cfg-if" 33 | version = "1.0.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 36 | 37 | [[package]] 38 | name = "const-random" 39 | version = "0.1.18" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" 42 | dependencies = [ 43 | "const-random-macro", 44 | ] 45 | 46 | [[package]] 47 | name = "const-random-macro" 48 | version = "0.1.16" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" 51 | dependencies = [ 52 | "getrandom", 53 | "once_cell", 54 | "tiny-keccak", 55 | ] 56 | 57 | [[package]] 58 | name = "crunchy" 59 | version = "0.2.3" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" 62 | 63 | [[package]] 64 | name = "getrandom" 65 | version = "0.2.15" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 68 | dependencies = [ 69 | "cfg-if", 70 | "libc", 71 | "wasi", 72 | ] 73 | 74 | [[package]] 75 | name = "heck" 76 | version = "0.5.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 79 | 80 | [[package]] 81 | name = "instant" 82 | version = "0.1.13" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 85 | dependencies = [ 86 | "cfg-if", 87 | ] 88 | 89 | [[package]] 90 | name = "inventory" 91 | version = "0.3.19" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "54b12ebb6799019b044deaf431eadfe23245b259bba5a2c0796acec3943a3cdb" 94 | dependencies = [ 95 | "rustversion", 96 | ] 97 | 98 | [[package]] 99 | name = "libc" 100 | version = "0.2.170" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" 103 | 104 | [[package]] 105 | name = "libloading" 106 | version = "0.8.6" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" 109 | dependencies = [ 110 | "cfg-if", 111 | "windows-targets", 112 | ] 113 | 114 | [[package]] 115 | name = "no-std-compat" 116 | version = "0.4.1" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" 119 | dependencies = [ 120 | "spin", 121 | ] 122 | 123 | [[package]] 124 | name = "num-traits" 125 | version = "0.2.19" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 128 | dependencies = [ 129 | "autocfg", 130 | ] 131 | 132 | [[package]] 133 | name = "once_cell" 134 | version = "1.20.3" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" 137 | dependencies = [ 138 | "portable-atomic", 139 | ] 140 | 141 | [[package]] 142 | name = "portable-atomic" 143 | version = "1.11.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" 146 | 147 | [[package]] 148 | name = "proc-macro2" 149 | version = "1.0.93" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 152 | dependencies = [ 153 | "unicode-ident", 154 | ] 155 | 156 | [[package]] 157 | name = "quote" 158 | version = "1.0.38" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 161 | dependencies = [ 162 | "proc-macro2", 163 | ] 164 | 165 | [[package]] 166 | name = "regex-lite" 167 | version = "0.1.6" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" 170 | 171 | [[package]] 172 | name = "rhai" 173 | version = "1.21.0" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "ce4d759a4729a655ddfdbb3ff6e77fb9eadd902dae12319455557796e435d2a6" 176 | dependencies = [ 177 | "ahash", 178 | "bitflags", 179 | "instant", 180 | "no-std-compat", 181 | "num-traits", 182 | "once_cell", 183 | "rhai_codegen", 184 | "smallvec", 185 | "smartstring", 186 | "thin-vec", 187 | ] 188 | 189 | [[package]] 190 | name = "rhai-dylib" 191 | version = "0.5.0" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "5996402b1af9424e355dbf1761d0605f0f08eca909609b445383369a2bc83f0a" 194 | dependencies = [ 195 | "libloading", 196 | "rhai", 197 | ] 198 | 199 | [[package]] 200 | name = "rhai_codegen" 201 | version = "2.2.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" 204 | dependencies = [ 205 | "proc-macro2", 206 | "quote", 207 | "syn", 208 | ] 209 | 210 | [[package]] 211 | name = "rhai_rustler" 212 | version = "1.2.0" 213 | dependencies = [ 214 | "rhai", 215 | "rhai-dylib", 216 | "rustler", 217 | "thiserror", 218 | ] 219 | 220 | [[package]] 221 | name = "rustler" 222 | version = "0.36.1" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "f04a7b61bf2db5495d6c0d2eb4b3f0f366864d47f2482834656e25d1b25fe290" 225 | dependencies = [ 226 | "inventory", 227 | "libloading", 228 | "regex-lite", 229 | "rustler_codegen", 230 | ] 231 | 232 | [[package]] 233 | name = "rustler_codegen" 234 | version = "0.36.1" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "bf9365a04e3a3a4d3136953d97c67fd0a9c036d36197917961551c2cc1ecb385" 237 | dependencies = [ 238 | "heck", 239 | "inventory", 240 | "proc-macro2", 241 | "quote", 242 | "syn", 243 | ] 244 | 245 | [[package]] 246 | name = "rustversion" 247 | version = "1.0.19" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" 250 | 251 | [[package]] 252 | name = "smallvec" 253 | version = "1.14.0" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 256 | 257 | [[package]] 258 | name = "smartstring" 259 | version = "1.0.1" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" 262 | dependencies = [ 263 | "autocfg", 264 | "static_assertions", 265 | "version_check", 266 | ] 267 | 268 | [[package]] 269 | name = "spin" 270 | version = "0.5.2" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 273 | 274 | [[package]] 275 | name = "static_assertions" 276 | version = "1.1.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 279 | 280 | [[package]] 281 | name = "syn" 282 | version = "2.0.98" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" 285 | dependencies = [ 286 | "proc-macro2", 287 | "quote", 288 | "unicode-ident", 289 | ] 290 | 291 | [[package]] 292 | name = "thin-vec" 293 | version = "0.2.13" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b" 296 | 297 | [[package]] 298 | name = "thiserror" 299 | version = "2.0.12" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 302 | dependencies = [ 303 | "thiserror-impl", 304 | ] 305 | 306 | [[package]] 307 | name = "thiserror-impl" 308 | version = "2.0.12" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 311 | dependencies = [ 312 | "proc-macro2", 313 | "quote", 314 | "syn", 315 | ] 316 | 317 | [[package]] 318 | name = "tiny-keccak" 319 | version = "2.0.2" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 322 | dependencies = [ 323 | "crunchy", 324 | ] 325 | 326 | [[package]] 327 | name = "unicode-ident" 328 | version = "1.0.17" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" 331 | 332 | [[package]] 333 | name = "version_check" 334 | version = "0.9.5" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 337 | 338 | [[package]] 339 | name = "wasi" 340 | version = "0.11.0+wasi-snapshot-preview1" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 343 | 344 | [[package]] 345 | name = "windows-targets" 346 | version = "0.52.6" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 349 | dependencies = [ 350 | "windows_aarch64_gnullvm", 351 | "windows_aarch64_msvc", 352 | "windows_i686_gnu", 353 | "windows_i686_gnullvm", 354 | "windows_i686_msvc", 355 | "windows_x86_64_gnu", 356 | "windows_x86_64_gnullvm", 357 | "windows_x86_64_msvc", 358 | ] 359 | 360 | [[package]] 361 | name = "windows_aarch64_gnullvm" 362 | version = "0.52.6" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 365 | 366 | [[package]] 367 | name = "windows_aarch64_msvc" 368 | version = "0.52.6" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 371 | 372 | [[package]] 373 | name = "windows_i686_gnu" 374 | version = "0.52.6" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 377 | 378 | [[package]] 379 | name = "windows_i686_gnullvm" 380 | version = "0.52.6" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 383 | 384 | [[package]] 385 | name = "windows_i686_msvc" 386 | version = "0.52.6" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 389 | 390 | [[package]] 391 | name = "windows_x86_64_gnu" 392 | version = "0.52.6" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 395 | 396 | [[package]] 397 | name = "windows_x86_64_gnullvm" 398 | version = "0.52.6" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 401 | 402 | [[package]] 403 | name = "windows_x86_64_msvc" 404 | version = "0.52.6" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 407 | 408 | [[package]] 409 | name = "zerocopy" 410 | version = "0.7.35" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 413 | dependencies = [ 414 | "zerocopy-derive", 415 | ] 416 | 417 | [[package]] 418 | name = "zerocopy-derive" 419 | version = "0.7.35" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 422 | dependencies = [ 423 | "proc-macro2", 424 | "quote", 425 | "syn", 426 | ] 427 | -------------------------------------------------------------------------------- /test/rhai/engine_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Rhai.EngineTest do 2 | use ExUnit.Case 3 | 4 | alias Rhai.{AST, Engine, Scope} 5 | 6 | describe "new/0" do 7 | test "should create a new engine" do 8 | assert %Engine{} = Engine.new() 9 | end 10 | end 11 | 12 | describe "new_raw/0" do 13 | test "should create a new raw engine" do 14 | assert %Engine{} = Engine.new_raw() 15 | end 16 | end 17 | 18 | describe "module resolvers" do 19 | test "should load a rhai module via the import directive" do 20 | assert {:ok, 42} = 21 | Engine.new_raw() 22 | |> Engine.set_module_resolvers([:file]) 23 | |> Engine.eval(""" 24 | import "#{File.cwd!()}/test/fixtures/script" as m; 25 | 26 | m::test(41) 27 | """) 28 | end 29 | 30 | test "should load a dylib module via the import directive" do 31 | assert {:ok, [6, "inner", "value"]} = 32 | Engine.new_raw() 33 | |> Engine.set_module_resolvers([:dylib]) 34 | |> Engine.eval(""" 35 | import "#{File.cwd!()}/priv/native/libtest_dylib_module" as plugin; 36 | 37 | let result = []; 38 | 39 | result += plugin::triple_add(1, 2, 3); 40 | result += plugin::new_plugin_object("inner").get_inner(); 41 | result += plugin::get_property(\#{ property: "value" }); 42 | 43 | result 44 | """) 45 | end 46 | end 47 | 48 | describe "register_global_module/2" do 49 | test "should register a module into the global namespace" do 50 | engine = Engine.new() 51 | 52 | assert {:ok, engine} = 53 | Engine.register_global_module( 54 | engine, 55 | "#{File.cwd!()}/priv/native/libtest_dylib_module" 56 | ) 57 | 58 | assert {:ok, [6, "inner", "value"]} = 59 | Engine.eval(engine, """ 60 | let result = []; 61 | 62 | result += triple_add(1, 2, 3); 63 | result += new_plugin_object("inner").get_inner(); 64 | result += get_property(\#{ property: "value" }); 65 | 66 | result 67 | """) 68 | end 69 | 70 | test "should return error if the module is not found" do 71 | assert {:error, {:runtime, _}} = 72 | Engine.new() 73 | |> Engine.register_global_module("non_existing_module") 74 | end 75 | end 76 | 77 | describe "register_global_module!/2" do 78 | test "should register a module into the global namespace" do 79 | assert {:ok, 6} = 80 | Engine.new() 81 | |> Engine.register_global_module!( 82 | File.cwd!() <> "/priv/native/libtest_dylib_module" 83 | ) 84 | |> Engine.eval("triple_add(1, 2, 3);") 85 | end 86 | 87 | test "should raise if the module is not found" do 88 | assert_raise RuntimeError, fn -> 89 | engine = Engine.new() 90 | 91 | Engine.register_global_module!(engine, "non_existing_module") 92 | end 93 | end 94 | end 95 | 96 | describe "register_static_module/3" do 97 | test "should register a static moudle namespace" do 98 | engine = Engine.new() 99 | 100 | assert {:ok, engine} = 101 | Engine.register_static_module( 102 | engine, 103 | "plugin", 104 | "#{File.cwd!()}/priv/native/libtest_dylib_module" 105 | ) 106 | 107 | assert {:ok, [6, "inner", "value"]} = 108 | Engine.eval(engine, """ 109 | let result = []; 110 | 111 | result += triple_add(1, 2, 3); 112 | result += new_plugin_object("inner").get_inner(); 113 | result += plugin::get_property(\#{ property: "value" }); 114 | 115 | result 116 | """) 117 | end 118 | 119 | test "should return error if the module is not found" do 120 | engine = Engine.new() 121 | 122 | assert {:error, {:runtime, _}} = 123 | Engine.register_static_module(engine, "plugin", "non_existing_module") 124 | end 125 | end 126 | 127 | describe "register_static_module!/3" do 128 | test "should register a static moudle namespace" do 129 | assert {:ok, "value"} = 130 | Engine.new() 131 | |> Engine.register_static_module!( 132 | "plugin", 133 | "#{File.cwd!()}/priv/native/libtest_dylib_module" 134 | ) 135 | |> Engine.eval("plugin::get_property(\#{ property: \"value\" });") 136 | end 137 | 138 | test "should raise if the module is not found" do 139 | assert_raise RuntimeError, fn -> 140 | engine = Engine.new() 141 | 142 | Engine.register_static_module!(engine, "plugin", "non_existing_module") 143 | end 144 | end 145 | end 146 | 147 | describe "register_custom_operator/3" do 148 | test "should register a custom operator" do 149 | assert {:ok, engine} = 150 | Engine.new() 151 | |> Engine.register_static_module!( 152 | "plugin", 153 | "#{File.cwd!()}/priv/native/libtest_dylib_module" 154 | ) 155 | |> Engine.register_custom_operator("#", 160) 156 | 157 | assert {:ok, 3} = Engine.eval(engine, "1 # 2") 158 | end 159 | 160 | test "should return error if the operator is reserved" do 161 | engine = Engine.new() 162 | 163 | assert {:error, {:custom_operator, "'+' is a reserved operator"}} = 164 | Engine.register_custom_operator(engine, "+", 160) 165 | end 166 | end 167 | 168 | describe "register_custom_operator!/3" do 169 | test "should register a custom operator" do 170 | assert {:ok, 3} = 171 | Engine.new() 172 | |> Engine.register_static_module!( 173 | "plugin", 174 | "#{File.cwd!()}/priv/native/libtest_dylib_module" 175 | ) 176 | |> Engine.register_custom_operator!("#", 160) 177 | |> Engine.eval("1 # 2") 178 | end 179 | 180 | test "should raise if the operator is reserved" do 181 | engine = Engine.new() 182 | 183 | assert_raise RuntimeError, fn -> 184 | Engine.register_custom_operator!(engine, "+", 160) 185 | end 186 | end 187 | end 188 | 189 | describe "register_package/2" do 190 | test "should register the standard package" do 191 | engine = Engine.new_raw() 192 | 193 | assert {:error, {:function_not_found, _}} = Engine.eval(engine, "[1, 2, 3].get(1)") 194 | 195 | assert {:ok, 2} = 196 | engine 197 | |> Engine.register_package(:standard) 198 | |> Engine.eval("[1, 2, 3].get(1)") 199 | end 200 | end 201 | 202 | describe "compile/2" do 203 | test "should compile a string into an AST" do 204 | engine = Engine.new() 205 | 206 | assert {:ok, %AST{}} = Engine.compile(engine, "1+1") 207 | end 208 | 209 | test "should not compile an invalid expression" do 210 | engine = Engine.new() 211 | 212 | assert {:error, {:parsing, _}} = Engine.compile(engine, "???") 213 | end 214 | end 215 | 216 | describe "compile_with_scope/3" do 217 | test "should compile a string into an AST with scope" do 218 | engine = Engine.new() 219 | 220 | scope = Scope.new() |> Scope.push_constant("a", 1) |> Scope.push_constant("b", 2) 221 | 222 | assert {:ok, %AST{} = ast} = 223 | Engine.compile_with_scope(engine, scope, "fn test (x) { a + b + x}; test(3)") 224 | 225 | assert {:ok, 6} = Engine.eval_ast(engine, ast) 226 | end 227 | end 228 | 229 | describe "compile_expression/2" do 230 | test "should compile an expression into an AST" do 231 | engine = Engine.new() 232 | 233 | assert {:ok, %AST{} = ast} = Engine.compile_expression(engine, "1 + 1") 234 | assert {:ok, 2} = Engine.eval_ast(engine, ast) 235 | end 236 | end 237 | 238 | describe "compile_expression_with_scope/3" do 239 | test "should compile an expression into an AST with scope" do 240 | engine = Engine.new() 241 | 242 | scope = Scope.new() |> Scope.push_constant("a", 1) |> Scope.push_constant("b", 2) 243 | 244 | assert {:ok, %AST{} = ast} = Engine.compile_expression_with_scope(engine, scope, "a + b") 245 | 246 | assert {:ok, 3} = Engine.eval_ast(engine, ast) 247 | end 248 | end 249 | 250 | describe "compile_file/2" do 251 | test "should compile a script file into an AST" do 252 | engine = Engine.new() 253 | 254 | assert {:ok, %AST{} = ast} = 255 | Engine.compile_file(engine, File.cwd!() <> "/test/fixtures/script.rhai") 256 | 257 | assert {:ok, 43} = Engine.eval_ast(engine, ast) 258 | end 259 | end 260 | 261 | describe "compile_file_with_scope/3" do 262 | test "should compile an expression into an AST with scope" do 263 | engine = Engine.new() 264 | 265 | scope = Scope.new() |> Scope.push_constant("a", 1) |> Scope.push_constant("b", 2) 266 | 267 | assert {:ok, %AST{} = ast} = 268 | Engine.compile_file_with_scope( 269 | engine, 270 | scope, 271 | File.cwd!() <> "/test/fixtures/script_with_scope.rhai" 272 | ) 273 | 274 | assert {:ok, 45} = Engine.eval_ast(engine, ast) 275 | end 276 | end 277 | 278 | describe "compile_into_self_contained/3" do 279 | test "should compile a script into an AST with scope embedding all imported modules" do 280 | engine = Engine.new_raw() 281 | 282 | scope = 283 | Scope.new() 284 | |> Scope.push_constant("a", 1) 285 | |> Scope.push_constant("b", 2) 286 | |> Scope.push_constant("c", 3) 287 | 288 | assert {:ok, %AST{} = ast} = 289 | engine 290 | |> Engine.set_module_resolvers([:dylib]) 291 | |> Engine.compile_into_self_contained(scope, """ 292 | import "#{File.cwd!()}/priv/native/libtest_dylib_module" as plugin; 293 | 294 | let result = []; 295 | 296 | result += plugin::triple_add(a, b, c); 297 | result += plugin::new_plugin_object("inner").get_inner(); 298 | result += plugin::get_property(\#{ property: "value" }); 299 | 300 | result 301 | """) 302 | 303 | assert {:ok, [6, "inner", "value"]} = Engine.eval_ast(engine, ast) 304 | end 305 | end 306 | 307 | describe "compact_script/2" do 308 | test "should compact a script" do 309 | engine = Engine.new() 310 | 311 | assert {:ok, "fn test(){a+b}"} = 312 | Engine.compact_script(engine, """ 313 | 314 | 315 | fn test() { a + b } 316 | 317 | 318 | """) 319 | end 320 | end 321 | 322 | describe "eval/1" do 323 | test "should eval a script" do 324 | engine = Engine.new() 325 | 326 | assert {:ok, 2} = Engine.eval(engine, "1 + 1") 327 | end 328 | end 329 | 330 | describe "eval_with_scope/3" do 331 | test "should eval a script with scope" do 332 | engine = Engine.new() 333 | scope = Scope.new() |> Scope.push_constant("a", 1) |> Scope.push("b", 1) 334 | 335 | assert {:ok, 2} = Engine.eval_with_scope(engine, scope, "a + b") 336 | end 337 | end 338 | 339 | describe "eval_ast/2" do 340 | test "should eval an AST" do 341 | engine = Engine.new() 342 | {:ok, ast} = Engine.compile(engine, "40 + 2") 343 | 344 | assert {:ok, 42} = Engine.eval_ast(engine, ast) 345 | end 346 | end 347 | 348 | describe "eval_ast_with_scope/3" do 349 | test "should eval an AST with scope" do 350 | engine = Engine.new() 351 | {:ok, ast} = Engine.compile(engine, "a + b") 352 | scope = Scope.new() |> Scope.push_constant("a", 1) |> Scope.push("b", 1) 353 | 354 | assert {:ok, 2} = Engine.eval_ast_with_scope(engine, scope, ast) 355 | end 356 | end 357 | 358 | describe "eval_expression/2" do 359 | test "should eval an expression" do 360 | engine = Engine.new() 361 | 362 | assert {:ok, 2} = Engine.eval_expression(engine, "1 + 1") 363 | end 364 | end 365 | 366 | describe "eval_expression_with_scope/3" do 367 | test "should eval an expression with scope" do 368 | engine = Engine.new() 369 | 370 | scope = Scope.new() |> Scope.push_constant("a", 1) |> Scope.push_constant("b", 1) 371 | 372 | assert {:ok, 2} = Engine.eval_expression_with_scope(engine, scope, "a + b") 373 | end 374 | end 375 | 376 | describe "eval_file/2" do 377 | test "should eval a script file" do 378 | engine = Engine.new() 379 | 380 | assert {:ok, 43} = Engine.eval_file(engine, File.cwd!() <> "/test/fixtures/script.rhai") 381 | end 382 | end 383 | 384 | describe "eval_file_with_scope/3" do 385 | test "should eval a script file with scope" do 386 | engine = Engine.new() 387 | 388 | scope = Scope.new() |> Scope.push_constant("a", 1) |> Scope.push_constant("b", 2) 389 | 390 | assert {:ok, 45} = 391 | Engine.eval_file_with_scope( 392 | engine, 393 | scope, 394 | File.cwd!() <> "/test/fixtures/script_with_scope.rhai" 395 | ) 396 | end 397 | end 398 | 399 | describe "run/2" do 400 | test "should run a script" do 401 | engine = Engine.new() 402 | 403 | assert :ok = Engine.run(engine, "40 + 2;") 404 | end 405 | end 406 | 407 | describe "run_with_scope/3" do 408 | test "should run a script with scope" do 409 | engine = Engine.new() 410 | scope = Scope.new() |> Scope.push("x", 40) 411 | 412 | assert :ok = Engine.run_with_scope(engine, scope, "x += 2;") 413 | assert 42 == Scope.get_value(scope, "x") 414 | end 415 | end 416 | 417 | describe "run_ast/2" do 418 | test "should run an AST" do 419 | engine = Engine.new() 420 | {:ok, ast} = Engine.compile(engine, "40 + 2;") 421 | 422 | assert :ok = Engine.run_ast(engine, ast) 423 | end 424 | end 425 | 426 | describe "run_ast_with_scope/3" do 427 | test "should run an AST with scope" do 428 | engine = Engine.new() 429 | scope = Scope.new() |> Scope.push("x", 40) 430 | 431 | {:ok, ast} = Engine.compile(engine, "x += 2;") 432 | 433 | assert :ok = Engine.run_ast_with_scope(engine, scope, ast) 434 | assert 42 == Scope.get_value(scope, "x") 435 | end 436 | end 437 | 438 | describe "run_file/2" do 439 | test "should run a script file" do 440 | engine = Engine.new() 441 | 442 | assert :ok = Engine.run_file(engine, File.cwd!() <> "/test/fixtures/script.rhai") 443 | end 444 | end 445 | 446 | describe "run_file_with_scope/3" do 447 | test "should run a script file with scope" do 448 | engine = Engine.new() 449 | 450 | scope = Scope.new() |> Scope.push_constant("a", 1) |> Scope.push_constant("b", 2) 451 | 452 | assert :ok = 453 | Engine.run_file_with_scope( 454 | engine, 455 | scope, 456 | File.cwd!() <> "/test/fixtures/script_with_scope.rhai" 457 | ) 458 | 459 | assert 45 == Scope.get_value(scope, "x") 460 | end 461 | end 462 | 463 | describe "call_fn/5" do 464 | test "should call a script function" do 465 | engine = Engine.new() 466 | {:ok, ast} = Engine.compile(engine, "fn test(x, y) { a + b + x + y }") 467 | scope = Scope.new() |> Scope.push("a", 1) |> Scope.push("b", 2) 468 | 469 | assert {:ok, 10} = Engine.call_fn(engine, scope, ast, "test", [3, 4]) 470 | end 471 | end 472 | 473 | describe "set_fail_on_invalid_map_property/2, fail_on_invalid_map_property?/1" do 474 | test "should return false by default" do 475 | engine = Engine.new() 476 | 477 | refute Engine.fail_on_invalid_map_property?(engine) 478 | end 479 | 480 | test "should set fail on invalid map property to enabled" do 481 | assert Engine.new() 482 | |> Engine.set_fail_on_invalid_map_property(true) 483 | |> Engine.fail_on_invalid_map_property?() 484 | end 485 | end 486 | 487 | describe "set_max_array_size/2, max_array_size/1" do 488 | test "should return an unlimited size by default" do 489 | engine = Engine.new() 490 | 491 | assert 0 == Engine.max_array_size(engine) 492 | end 493 | 494 | test "should set a max array size" do 495 | assert 256 = Engine.new() |> Engine.set_max_array_size(256) |> Engine.max_array_size() 496 | end 497 | end 498 | 499 | describe "set_allow_anonymous_fn/2, allow_anonymous_fn?/1" do 500 | test "should return true by default" do 501 | engine = Engine.new() 502 | 503 | assert Engine.allow_anonymous_fn?(engine) 504 | end 505 | 506 | test "should set the flag to false" do 507 | refute Engine.new() |> Engine.set_allow_anonymous_fn(false) |> Engine.allow_anonymous_fn?() 508 | end 509 | end 510 | 511 | describe "set_allow_if_expression/2, allow_if_expression?/1" do 512 | test "should return true by default" do 513 | engine = Engine.new() 514 | 515 | assert Engine.allow_if_expression?(engine) 516 | end 517 | 518 | test "should set the flag to false" do 519 | refute Engine.new() 520 | |> Engine.set_allow_if_expression(false) 521 | |> Engine.allow_if_expression?() 522 | end 523 | end 524 | 525 | describe "set_allow_loop_expressions/2, allow_loop_expressions/1" do 526 | test "should return true by default" do 527 | engine = Engine.new() 528 | 529 | assert Engine.allow_loop_expressions?(engine) 530 | end 531 | 532 | test "should set the flag to false" do 533 | refute Engine.new() 534 | |> Engine.set_allow_loop_expressions(false) 535 | |> Engine.allow_loop_expressions?() 536 | end 537 | end 538 | 539 | describe "set_allow_looping/2, allow_looping?/1" do 540 | test "should return true by default" do 541 | engine = Engine.new() 542 | 543 | assert Engine.allow_looping?(engine) 544 | end 545 | 546 | test "should set the flag to false" do 547 | refute Engine.new() |> Engine.set_allow_looping(false) |> Engine.allow_looping?() 548 | end 549 | end 550 | 551 | describe "set_allow_shadowing/2, allow_shadowing?/1" do 552 | test "should return true by default" do 553 | engine = Engine.new() 554 | 555 | assert Engine.allow_shadowing?(engine) 556 | end 557 | 558 | test "should set the flag to false" do 559 | refute Engine.new() |> Engine.set_allow_shadowing(false) |> Engine.allow_shadowing?() 560 | end 561 | end 562 | 563 | describe "set_allow_statement_expression/2, allow_statement_expression?/1" do 564 | test "should return true by default" do 565 | engine = Engine.new() 566 | 567 | assert Engine.allow_statement_expression?(engine) 568 | end 569 | 570 | test "should set the flag to false" do 571 | refute Engine.new() 572 | |> Engine.set_allow_statement_expression(false) 573 | |> Engine.allow_statement_expression?() 574 | end 575 | end 576 | 577 | describe "set_allow_switch_expression/2, allow_switch_expression?/1" do 578 | test "should return true by default" do 579 | engine = Engine.new() 580 | 581 | assert Engine.allow_switch_expression?(engine) 582 | end 583 | 584 | test "should set the flag to false" do 585 | refute Engine.new() 586 | |> Engine.set_allow_switch_expression(false) 587 | |> Engine.allow_switch_expression?() 588 | end 589 | end 590 | 591 | describe "set_fast_operators/2, fast_operators?/1" do 592 | test "should return true by default" do 593 | engine = Engine.new() 594 | 595 | assert Engine.fast_operators?(engine) 596 | end 597 | 598 | test "should set the flag to false" do 599 | refute Engine.new() 600 | |> Engine.set_fast_operators(false) 601 | |> Engine.fast_operators?() 602 | end 603 | end 604 | 605 | describe "set_allow_max_call_levels/2, allow_max_call_levels/1" do 606 | test "should return true by default" do 607 | engine = Engine.new() 608 | 609 | assert 64 == Engine.max_call_levels(engine) 610 | end 611 | 612 | test "should set the flag to false" do 613 | assert 256 == 614 | Engine.new() 615 | |> Engine.set_max_call_levels(256) 616 | |> Engine.max_call_levels() 617 | end 618 | end 619 | 620 | describe "set_max_call_levels/2, max_call_levels/1" do 621 | test "should return 64 by default" do 622 | engine = Engine.new() 623 | 624 | assert 64 == Engine.max_call_levels(engine) 625 | end 626 | 627 | test "should set the size to 256" do 628 | assert 256 == 629 | Engine.new() 630 | |> Engine.set_max_call_levels(256) 631 | |> Engine.max_call_levels() 632 | end 633 | end 634 | 635 | describe "set_max_expr_depths/3, max_expr_depth/1, max_function_expr_depth/1" do 636 | test "should return 64 by default" do 637 | engine = Engine.new() 638 | 639 | assert 64 == Engine.max_expr_depth(engine) 640 | end 641 | 642 | test "should set expr depth" do 643 | assert 256 == 644 | Engine.new() 645 | |> Engine.set_max_expr_depths(256, 512) 646 | |> Engine.max_expr_depth() 647 | end 648 | 649 | test "should set max function expr depth" do 650 | assert 512 == 651 | Engine.new() 652 | |> Engine.set_max_expr_depths(256, 512) 653 | |> Engine.max_function_expr_depth() 654 | end 655 | end 656 | 657 | describe "set_max_map_size/2, max_map_size/1" do 658 | test "should return 0 by default" do 659 | engine = Engine.new() 660 | 661 | assert 0 == Engine.max_map_size(engine) 662 | end 663 | 664 | test "should set the size to 256" do 665 | assert 256 == 666 | Engine.new() 667 | |> Engine.set_max_map_size(256) 668 | |> Engine.max_map_size() 669 | end 670 | end 671 | 672 | describe "set_max_modules/2, max_modules/1" do 673 | test "should return the default" do 674 | engine = Engine.new() 675 | 676 | assert 18_446_744_073_709_551_615 == Engine.max_modules(engine) 677 | end 678 | 679 | test "should set modules number to 256" do 680 | assert 256 == 681 | Engine.new() 682 | |> Engine.set_max_modules(256) 683 | |> Engine.max_modules() 684 | end 685 | end 686 | 687 | describe "set_max_operations/2, max_operations/1" do 688 | test "should return 0 by default" do 689 | engine = Engine.new() 690 | 691 | assert 0 == Engine.max_operations(engine) 692 | end 693 | 694 | test "should set ops limit to 256" do 695 | assert 256 == 696 | Engine.new() 697 | |> Engine.set_max_operations(256) 698 | |> Engine.max_operations() 699 | end 700 | end 701 | 702 | describe "set_max_string_size/2, max_string_size/1" do 703 | test "should return 0 by default" do 704 | engine = Engine.new() 705 | 706 | assert 0 == Engine.max_string_size(engine) 707 | end 708 | 709 | test "should set string size limit to 256" do 710 | assert 256 == 711 | Engine.new() 712 | |> Engine.set_max_string_size(256) 713 | |> Engine.max_string_size() 714 | end 715 | end 716 | 717 | describe "set_strict_variables/2, strict_variables/1" do 718 | test "should return false by default" do 719 | engine = Engine.new() 720 | 721 | refute Engine.strict_variables?(engine) 722 | end 723 | 724 | test "should set strict variables mode to true" do 725 | assert Engine.new() 726 | |> Engine.set_strict_variables(true) 727 | |> Engine.strict_variables?() 728 | end 729 | end 730 | 731 | describe "set_optimization_level/2, optimization_level/1" do 732 | test "should return :simple by default" do 733 | engine = Engine.new() 734 | 735 | assert :simple == Engine.optimization_level(engine) 736 | end 737 | 738 | test "should set the optimization level" do 739 | optimization_level = Enum.random([:full, :simple, :none]) 740 | 741 | assert optimization_level == 742 | Engine.new() 743 | |> Engine.set_optimization_level(optimization_level) 744 | |> Engine.optimization_level() 745 | end 746 | end 747 | 748 | describe "optimize_ast/4" do 749 | test "should optimize an AST" do 750 | engine = Engine.new() 751 | 752 | scope = Scope.new() |> Scope.push_constant("a", 1) |> Scope.push_constant("b", 2) 753 | 754 | {:ok, ast} = Engine.compile(engine, "a + b") 755 | 756 | assert %AST{} = optimized_ast = Engine.optimize_ast(engine, scope, ast, :full) 757 | assert {:ok, 3} = Engine.eval_ast_with_scope(engine, scope, optimized_ast) 758 | end 759 | end 760 | 761 | describe "disable_symbol/2" do 762 | test "should disable a keyword" do 763 | assert {:error, {:parsing, "'if' is a reserved keyword (line 1, position 9)"}} = 764 | Engine.new() 765 | |> Engine.disable_symbol("if") 766 | |> Engine.compile("let x = if true { 42 } else { 0 };") 767 | end 768 | 769 | test "should disable an operator" do 770 | assert {:error, {:parsing, "Unknown operator: '+' (line 1, position 11)"}} = 771 | Engine.new() 772 | |> Engine.disable_symbol("+") 773 | |> Engine.compile("let x = 1 + 2;") 774 | end 775 | end 776 | 777 | describe "ensure_data_size_within_limits/2" do 778 | test "should not return an error if the data size is within limits" do 779 | assert :ok = 780 | Engine.new() 781 | |> Engine.set_max_array_size(2) 782 | |> Engine.ensure_data_size_within_limits("[1, 2]") 783 | end 784 | 785 | test "should return error if the data size is too big" do 786 | assert {:error, {:data_too_large, "Length of string too large"}} = 787 | Engine.new() 788 | |> Engine.set_max_string_size(1) 789 | |> Engine.ensure_data_size_within_limits("[1, 2]") 790 | end 791 | end 792 | end 793 | -------------------------------------------------------------------------------- /native/rhai_rustler/src/engine.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Mutex; 2 | 3 | use rhai::{ 4 | module_resolvers::{FileModuleResolver, ModuleResolversCollection}, 5 | packages::Package as RhaiPackage, 6 | Dynamic, Engine, 7 | }; 8 | 9 | use rhai_dylib::loader::{libloading::Libloading, Loader}; 10 | use rhai_dylib::module_resolvers::libloading::DylibModuleResolver; 11 | 12 | use rustler::{Env, NifUnitEnum, Resource, ResourceArc, Term}; 13 | 14 | use crate::{ 15 | ast::ASTResource, 16 | error::RhaiRustlerError, 17 | scope::ScopeResource, 18 | types::{from_dynamic, to_dynamic}, 19 | }; 20 | 21 | #[cfg(target_os = "linux")] 22 | const DYLIB_EXTENSION: &str = "so"; 23 | #[cfg(target_os = "macos")] 24 | const DYLIB_EXTENSION: &str = "dylib"; 25 | #[cfg(target_os = "windows")] 26 | const DYLIB_EXTENSION: &str = "dll"; 27 | 28 | pub struct EngineResource { 29 | pub engine: Mutex, 30 | } 31 | 32 | #[rustler::resource_impl] 33 | impl Resource for EngineResource {} 34 | 35 | #[rustler::nif] 36 | fn engine_new() -> ResourceArc { 37 | let engine = Engine::new(); 38 | 39 | ResourceArc::new(EngineResource { 40 | engine: Mutex::new(engine), 41 | }) 42 | } 43 | 44 | #[rustler::nif] 45 | fn engine_new_raw() -> ResourceArc { 46 | let engine = Engine::new_raw(); 47 | 48 | ResourceArc::new(EngineResource { 49 | engine: Mutex::new(engine), 50 | }) 51 | } 52 | 53 | #[derive(NifUnitEnum)] 54 | enum ModuleResolver { 55 | File, 56 | Dylib, 57 | } 58 | 59 | #[rustler::nif] 60 | fn engine_set_module_resolvers( 61 | resource: ResourceArc, 62 | module_resolvers: Vec, 63 | ) { 64 | let mut engine = resource.engine.try_lock().unwrap(); 65 | let mut resolvers_collection = ModuleResolversCollection::new(); 66 | 67 | for module_resolver in module_resolvers { 68 | match module_resolver { 69 | ModuleResolver::File => resolvers_collection.push(FileModuleResolver::new()), 70 | ModuleResolver::Dylib => resolvers_collection.push(DylibModuleResolver::new()), 71 | }; 72 | } 73 | 74 | engine.set_module_resolver(resolvers_collection); 75 | } 76 | 77 | #[rustler::nif] 78 | fn engine_register_global_module( 79 | resource: ResourceArc, 80 | path: String, 81 | ) -> Result<(), RhaiRustlerError> { 82 | let mut engine = resource.engine.try_lock().unwrap(); 83 | let mut loader = Libloading::new(); 84 | 85 | let path = format!("{}.{}", path, DYLIB_EXTENSION); 86 | 87 | engine.register_global_module(loader.load(path)?); 88 | 89 | Ok(()) 90 | } 91 | 92 | #[rustler::nif] 93 | fn engine_register_static_module( 94 | resource: ResourceArc, 95 | namespace: String, 96 | path: String, 97 | ) -> Result<(), RhaiRustlerError> { 98 | let mut engine = resource.engine.try_lock().unwrap(); 99 | let mut loader = Libloading::new(); 100 | 101 | let path = format!("{}.{}", path, DYLIB_EXTENSION); 102 | 103 | engine.register_static_module(namespace, loader.load(path)?); 104 | 105 | Ok(()) 106 | } 107 | 108 | #[rustler::nif] 109 | fn engine_register_custom_operator( 110 | resource: ResourceArc, 111 | keyword: &str, 112 | precedence: u8, 113 | ) -> Result<(), RhaiRustlerError> { 114 | let mut engine = resource.engine.try_lock().unwrap(); 115 | 116 | match engine.register_custom_operator(keyword, precedence) { 117 | Ok(_) => Ok(()), 118 | Err(message) => Err(RhaiRustlerError::CustomOperator { message }), 119 | } 120 | } 121 | 122 | #[derive(NifUnitEnum)] 123 | enum Package { 124 | Arithmetic, 125 | BasicArray, 126 | BasicBlob, 127 | BasicFn, 128 | BasicIterator, 129 | BasicMap, 130 | BasicMath, 131 | BasicString, 132 | BasicTime, 133 | BitField, 134 | Core, 135 | LanguageCore, 136 | Logic, 137 | MoreString, 138 | Standard, 139 | } 140 | 141 | #[rustler::nif] 142 | fn engine_register_package(resource: ResourceArc, package: Package) { 143 | let mut engine = resource.engine.try_lock().unwrap(); 144 | 145 | match package { 146 | Package::Arithmetic => { 147 | let package = rhai::packages::ArithmeticPackage::new(); 148 | package.register_into_engine(&mut engine); 149 | } 150 | Package::BasicArray => { 151 | let package = rhai::packages::BasicArrayPackage::new(); 152 | package.register_into_engine(&mut engine); 153 | } 154 | Package::BasicBlob => { 155 | let package = rhai::packages::BasicBlobPackage::new(); 156 | package.register_into_engine(&mut engine); 157 | } 158 | Package::BasicFn => { 159 | let package = rhai::packages::BasicFnPackage::new(); 160 | package.register_into_engine(&mut engine); 161 | } 162 | Package::BasicIterator => { 163 | let package = rhai::packages::BasicIteratorPackage::new(); 164 | package.register_into_engine(&mut engine); 165 | } 166 | Package::BasicMap => { 167 | let package = rhai::packages::BasicMapPackage::new(); 168 | package.register_into_engine(&mut engine); 169 | } 170 | Package::BasicMath => { 171 | let package = rhai::packages::BasicMathPackage::new(); 172 | package.register_into_engine(&mut engine); 173 | } 174 | Package::BasicString => { 175 | let package = rhai::packages::BasicStringPackage::new(); 176 | package.register_into_engine(&mut engine); 177 | } 178 | Package::BasicTime => { 179 | let package = rhai::packages::BasicTimePackage::new(); 180 | package.register_into_engine(&mut engine); 181 | } 182 | Package::BitField => { 183 | let package = rhai::packages::BitFieldPackage::new(); 184 | package.register_into_engine(&mut engine); 185 | } 186 | Package::Core => { 187 | let package = rhai::packages::CorePackage::new(); 188 | package.register_into_engine(&mut engine); 189 | } 190 | Package::LanguageCore => { 191 | let package = rhai::packages::LanguageCorePackage::new(); 192 | package.register_into_engine(&mut engine); 193 | } 194 | Package::Logic => { 195 | let package = rhai::packages::LogicPackage::new(); 196 | package.register_into_engine(&mut engine); 197 | } 198 | Package::MoreString => { 199 | let package = rhai::packages::MoreStringPackage::new(); 200 | package.register_into_engine(&mut engine); 201 | } 202 | Package::Standard => { 203 | let package = rhai::packages::StandardPackage::new(); 204 | package.register_into_engine(&mut engine); 205 | } 206 | }; 207 | } 208 | 209 | #[rustler::nif(schedule = "DirtyCpu")] 210 | fn engine_compile( 211 | resource: ResourceArc, 212 | script: &str, 213 | ) -> Result, RhaiRustlerError> { 214 | let engine = resource.engine.try_lock().unwrap(); 215 | let ast = engine.compile(script)?; 216 | 217 | let ast_resource = ResourceArc::new(ASTResource { 218 | ast: Mutex::new(ast), 219 | }); 220 | 221 | Ok(ast_resource) 222 | } 223 | 224 | #[rustler::nif(schedule = "DirtyCpu")] 225 | fn engine_compile_with_scope( 226 | resource: ResourceArc, 227 | scope_resource: ResourceArc, 228 | script: &str, 229 | ) -> Result, RhaiRustlerError> { 230 | let engine = resource.engine.try_lock().unwrap(); 231 | let scope = scope_resource.scope.try_lock().unwrap(); 232 | let ast = engine.compile_with_scope(&scope, script)?; 233 | 234 | let ast_resource = ResourceArc::new(ASTResource { 235 | ast: Mutex::new(ast), 236 | }); 237 | 238 | Ok(ast_resource) 239 | } 240 | 241 | #[rustler::nif(schedule = "DirtyCpu")] 242 | fn engine_compile_expression( 243 | resource: ResourceArc, 244 | expression: &str, 245 | ) -> Result, RhaiRustlerError> { 246 | let engine = resource.engine.try_lock().unwrap(); 247 | let ast = engine.compile_expression(expression)?; 248 | 249 | let ast_resource = ResourceArc::new(ASTResource { 250 | ast: Mutex::new(ast), 251 | }); 252 | 253 | Ok(ast_resource) 254 | } 255 | 256 | #[rustler::nif(schedule = "DirtyCpu")] 257 | fn engine_compile_expression_with_scope( 258 | resource: ResourceArc, 259 | scope_resource: ResourceArc, 260 | expression: &str, 261 | ) -> Result, RhaiRustlerError> { 262 | let engine = resource.engine.try_lock().unwrap(); 263 | let scope = scope_resource.scope.try_lock().unwrap(); 264 | let ast = engine.compile_expression_with_scope(&scope, expression)?; 265 | 266 | let ast_resource = ResourceArc::new(ASTResource { 267 | ast: Mutex::new(ast), 268 | }); 269 | 270 | Ok(ast_resource) 271 | } 272 | 273 | #[rustler::nif(schedule = "DirtyCpu")] 274 | fn engine_compile_file( 275 | resource: ResourceArc, 276 | path: &str, 277 | ) -> Result, RhaiRustlerError> { 278 | let engine = resource.engine.try_lock().unwrap(); 279 | let ast = engine.compile_file(path.into())?; 280 | 281 | let ast_resource = ResourceArc::new(ASTResource { 282 | ast: Mutex::new(ast), 283 | }); 284 | 285 | Ok(ast_resource) 286 | } 287 | 288 | #[rustler::nif(schedule = "DirtyCpu")] 289 | fn engine_compile_file_with_scope( 290 | resource: ResourceArc, 291 | scope_resource: ResourceArc, 292 | path: &str, 293 | ) -> Result, RhaiRustlerError> { 294 | let engine = resource.engine.try_lock().unwrap(); 295 | let scope = scope_resource.scope.try_lock().unwrap(); 296 | let ast = engine.compile_file_with_scope(&scope, path.into())?; 297 | 298 | let ast_resource = ResourceArc::new(ASTResource { 299 | ast: Mutex::new(ast), 300 | }); 301 | 302 | Ok(ast_resource) 303 | } 304 | 305 | #[rustler::nif(schedule = "DirtyCpu")] 306 | fn engine_compile_into_self_contained( 307 | resource: ResourceArc, 308 | scope_resource: ResourceArc, 309 | script: &str, 310 | ) -> Result, RhaiRustlerError> { 311 | let engine = resource.engine.try_lock().unwrap(); 312 | let scope = scope_resource.scope.try_lock().unwrap(); 313 | let ast = engine.compile_into_self_contained(&scope, script)?; 314 | 315 | let ast_resource = ResourceArc::new(ASTResource { 316 | ast: Mutex::new(ast), 317 | }); 318 | 319 | Ok(ast_resource) 320 | } 321 | 322 | #[rustler::nif(schedule = "DirtyCpu")] 323 | fn engine_compile_scripts_with_scope( 324 | resource: ResourceArc, 325 | scope_resource: ResourceArc, 326 | scripts: Vec, 327 | ) -> Result, RhaiRustlerError> { 328 | let engine = resource.engine.try_lock().unwrap(); 329 | let scope = scope_resource.scope.try_lock().unwrap(); 330 | let ast = engine.compile_scripts_with_scope(&scope, scripts)?; 331 | 332 | let ast_resource = ResourceArc::new(ASTResource { 333 | ast: Mutex::new(ast), 334 | }); 335 | 336 | Ok(ast_resource) 337 | } 338 | 339 | #[rustler::nif(schedule = "DirtyCpu")] 340 | fn engine_compact_script( 341 | resource: ResourceArc, 342 | script: &str, 343 | ) -> Result { 344 | let engine = resource.engine.try_lock().unwrap(); 345 | let result = engine.compact_script(script)?; 346 | 347 | Ok(result) 348 | } 349 | 350 | #[rustler::nif(schedule = "DirtyCpu")] 351 | fn engine_eval<'a>( 352 | env: Env<'a>, 353 | resource: ResourceArc, 354 | script: &str, 355 | ) -> Result, RhaiRustlerError> { 356 | let engine = resource.engine.try_lock().unwrap(); 357 | let result = engine.eval::(script)?; 358 | 359 | Ok(from_dynamic(env, result)) 360 | } 361 | 362 | #[rustler::nif(schedule = "DirtyCpu")] 363 | fn engine_eval_with_scope<'a>( 364 | env: Env<'a>, 365 | engine_resource: ResourceArc, 366 | scope_resource: ResourceArc, 367 | script: &str, 368 | ) -> Result, RhaiRustlerError> { 369 | let engine = engine_resource.engine.try_lock().unwrap(); 370 | let mut scope = scope_resource.scope.try_lock().unwrap(); 371 | let result = engine.eval_with_scope::(&mut scope, script)?; 372 | 373 | Ok(from_dynamic(env, result)) 374 | } 375 | 376 | #[rustler::nif(schedule = "DirtyCpu")] 377 | fn engine_eval_ast( 378 | env: Env, 379 | engine_resource: ResourceArc, 380 | ast_resource: ResourceArc, 381 | ) -> Result { 382 | let engine = engine_resource.engine.try_lock().unwrap(); 383 | let ast = ast_resource.ast.try_lock().unwrap(); 384 | 385 | let result = engine.eval_ast(&ast)?; 386 | 387 | Ok(from_dynamic(env, result)) 388 | } 389 | 390 | #[rustler::nif(schedule = "DirtyCpu")] 391 | fn engine_eval_ast_with_scope( 392 | env: Env, 393 | engine_resource: ResourceArc, 394 | scope_resource: ResourceArc, 395 | ast_resource: ResourceArc, 396 | ) -> Result { 397 | let engine = engine_resource.engine.try_lock().unwrap(); 398 | let mut scope = scope_resource.scope.try_lock().unwrap(); 399 | let ast = ast_resource.ast.try_lock().unwrap(); 400 | 401 | let result = engine.eval_ast_with_scope::(&mut scope, &ast)?; 402 | 403 | Ok(from_dynamic(env, result)) 404 | } 405 | 406 | #[rustler::nif(schedule = "DirtyCpu")] 407 | fn engine_eval_expression<'a>( 408 | env: Env<'a>, 409 | resource: ResourceArc, 410 | expression: &str, 411 | ) -> Result, RhaiRustlerError> { 412 | let engine = resource.engine.try_lock().unwrap(); 413 | let result = engine.eval_expression::(expression)?; 414 | 415 | Ok(from_dynamic(env, result)) 416 | } 417 | 418 | #[rustler::nif(schedule = "DirtyCpu")] 419 | fn engine_eval_expression_with_scope<'a>( 420 | env: Env<'a>, 421 | resource: ResourceArc, 422 | scope_resource: ResourceArc, 423 | expression: &str, 424 | ) -> Result, RhaiRustlerError> { 425 | let engine = resource.engine.try_lock().unwrap(); 426 | let mut scope = scope_resource.scope.try_lock().unwrap(); 427 | let result = engine.eval_expression_with_scope::(&mut scope, expression)?; 428 | 429 | Ok(from_dynamic(env, result)) 430 | } 431 | 432 | #[rustler::nif(schedule = "DirtyCpu")] 433 | fn engine_eval_file<'a>( 434 | env: Env<'a>, 435 | resource: ResourceArc, 436 | path: &str, 437 | ) -> Result, RhaiRustlerError> { 438 | let engine = resource.engine.try_lock().unwrap(); 439 | let result = engine.eval_file::(path.into())?; 440 | 441 | Ok(from_dynamic(env, result)) 442 | } 443 | 444 | #[rustler::nif(schedule = "DirtyCpu")] 445 | fn engine_eval_file_with_scope<'a>( 446 | env: Env<'a>, 447 | resource: ResourceArc, 448 | scope_resource: ResourceArc, 449 | path: &str, 450 | ) -> Result, RhaiRustlerError> { 451 | let engine = resource.engine.try_lock().unwrap(); 452 | let mut scope = scope_resource.scope.try_lock().unwrap(); 453 | let result = engine.eval_file_with_scope::(&mut scope, path.into())?; 454 | 455 | Ok(from_dynamic(env, result)) 456 | } 457 | 458 | #[rustler::nif(schedule = "DirtyCpu")] 459 | fn engine_run(resource: ResourceArc, script: &str) -> Result<(), RhaiRustlerError> { 460 | let engine = resource.engine.try_lock().unwrap(); 461 | engine.run(script)?; 462 | 463 | Ok(()) 464 | } 465 | 466 | #[rustler::nif(schedule = "DirtyCpu")] 467 | fn engine_run_with_scope( 468 | resource: ResourceArc, 469 | scope_resource: ResourceArc, 470 | script: &str, 471 | ) -> Result<(), RhaiRustlerError> { 472 | let engine = resource.engine.try_lock().unwrap(); 473 | let mut scope = scope_resource.scope.try_lock().unwrap(); 474 | 475 | engine.run_with_scope(&mut scope, script)?; 476 | 477 | Ok(()) 478 | } 479 | 480 | #[rustler::nif(schedule = "DirtyCpu")] 481 | fn engine_run_ast( 482 | resource: ResourceArc, 483 | ast_resource: ResourceArc, 484 | ) -> Result<(), RhaiRustlerError> { 485 | let engine = resource.engine.try_lock().unwrap(); 486 | let ast = ast_resource.ast.try_lock().unwrap(); 487 | 488 | engine.run_ast(&ast)?; 489 | 490 | Ok(()) 491 | } 492 | 493 | #[rustler::nif(schedule = "DirtyCpu")] 494 | fn engine_run_ast_with_scope( 495 | resource: ResourceArc, 496 | scope_resource: ResourceArc, 497 | ast_resource: ResourceArc, 498 | ) -> Result<(), RhaiRustlerError> { 499 | let engine = resource.engine.try_lock().unwrap(); 500 | let mut scope = scope_resource.scope.try_lock().unwrap(); 501 | let ast = ast_resource.ast.try_lock().unwrap(); 502 | 503 | engine.run_ast_with_scope(&mut scope, &ast)?; 504 | 505 | Ok(()) 506 | } 507 | 508 | #[rustler::nif(schedule = "DirtyCpu")] 509 | fn engine_run_file( 510 | resource: ResourceArc, 511 | path: &str, 512 | ) -> Result<(), RhaiRustlerError> { 513 | let engine = resource.engine.try_lock().unwrap(); 514 | engine.run_file(path.into())?; 515 | 516 | Ok(()) 517 | } 518 | 519 | #[rustler::nif(schedule = "DirtyCpu")] 520 | fn engine_run_file_with_scope( 521 | resource: ResourceArc, 522 | scope_resource: ResourceArc, 523 | path: &str, 524 | ) -> Result<(), RhaiRustlerError> { 525 | let engine = resource.engine.try_lock().unwrap(); 526 | let mut scope = scope_resource.scope.try_lock().unwrap(); 527 | 528 | engine.run_file_with_scope(&mut scope, path.into())?; 529 | 530 | Ok(()) 531 | } 532 | 533 | #[rustler::nif(schedule = "DirtyCpu")] 534 | fn engine_call_fn<'a>( 535 | env: Env<'a>, 536 | resource: ResourceArc, 537 | scope: ResourceArc, 538 | ast: ResourceArc, 539 | name: &str, 540 | args: Vec>, 541 | ) -> Result, RhaiRustlerError> { 542 | let engine = resource.engine.try_lock().unwrap(); 543 | let mut scope = scope.scope.try_lock().unwrap(); 544 | let ast = ast.ast.try_lock().unwrap(); 545 | 546 | let args: Vec = args.into_iter().map(|arg| to_dynamic(env, &arg)).collect(); 547 | 548 | let result = engine.call_fn(&mut scope, &ast, name, args)?; 549 | 550 | Ok(from_dynamic(env, result)) 551 | } 552 | 553 | #[rustler::nif] 554 | fn engine_set_allow_anonymous_fn(resource: ResourceArc, enable: bool) { 555 | let mut engine = resource.engine.try_lock().unwrap(); 556 | 557 | engine.set_allow_anonymous_fn(enable); 558 | } 559 | 560 | #[rustler::nif] 561 | fn engine_allow_anonymous_fn(resource: ResourceArc) -> bool { 562 | let engine = resource.engine.try_lock().unwrap(); 563 | 564 | engine.allow_anonymous_fn() 565 | } 566 | 567 | #[rustler::nif] 568 | fn engine_set_allow_if_expression(resource: ResourceArc, enable: bool) { 569 | let mut engine = resource.engine.try_lock().unwrap(); 570 | 571 | engine.set_allow_if_expression(enable); 572 | } 573 | 574 | #[rustler::nif] 575 | fn engine_allow_if_expression(resource: ResourceArc) -> bool { 576 | let engine = resource.engine.try_lock().unwrap(); 577 | 578 | engine.allow_if_expression() 579 | } 580 | 581 | #[rustler::nif] 582 | fn engine_set_allow_loop_expressions(resource: ResourceArc, enable: bool) { 583 | let mut engine = resource.engine.try_lock().unwrap(); 584 | 585 | engine.set_allow_loop_expressions(enable); 586 | } 587 | 588 | #[rustler::nif] 589 | fn engine_allow_loop_expressions(resource: ResourceArc) -> bool { 590 | let engine = resource.engine.try_lock().unwrap(); 591 | 592 | engine.allow_loop_expressions() 593 | } 594 | 595 | #[rustler::nif] 596 | fn engine_set_allow_looping(resource: ResourceArc, enable: bool) { 597 | let mut engine = resource.engine.try_lock().unwrap(); 598 | 599 | engine.set_allow_looping(enable); 600 | } 601 | 602 | #[rustler::nif] 603 | fn engine_allow_looping(resource: ResourceArc) -> bool { 604 | let engine = resource.engine.try_lock().unwrap(); 605 | 606 | engine.allow_looping() 607 | } 608 | 609 | #[rustler::nif] 610 | fn engine_set_allow_shadowing(resource: ResourceArc, enable: bool) { 611 | let mut engine = resource.engine.try_lock().unwrap(); 612 | 613 | engine.set_allow_shadowing(enable); 614 | } 615 | 616 | #[rustler::nif] 617 | fn engine_allow_shadowing(resource: ResourceArc) -> bool { 618 | let engine = resource.engine.try_lock().unwrap(); 619 | 620 | engine.allow_shadowing() 621 | } 622 | 623 | #[rustler::nif] 624 | fn engine_set_allow_statement_expression(resource: ResourceArc, enable: bool) { 625 | let mut engine = resource.engine.try_lock().unwrap(); 626 | 627 | engine.set_allow_statement_expression(enable); 628 | } 629 | 630 | #[rustler::nif] 631 | fn engine_allow_statement_expression(resource: ResourceArc) -> bool { 632 | let engine = resource.engine.try_lock().unwrap(); 633 | 634 | engine.allow_statement_expression() 635 | } 636 | 637 | #[rustler::nif] 638 | fn engine_set_allow_switch_expression(resource: ResourceArc, enable: bool) { 639 | let mut engine = resource.engine.try_lock().unwrap(); 640 | 641 | engine.set_allow_switch_expression(enable); 642 | } 643 | 644 | #[rustler::nif] 645 | fn engine_allow_switch_expression(resource: ResourceArc) -> bool { 646 | let engine = resource.engine.try_lock().unwrap(); 647 | 648 | engine.allow_switch_expression() 649 | } 650 | 651 | #[rustler::nif] 652 | fn engine_set_fail_on_invalid_map_property(resource: ResourceArc, enable: bool) { 653 | let mut engine = resource.engine.try_lock().unwrap(); 654 | 655 | engine.set_fail_on_invalid_map_property(enable); 656 | } 657 | 658 | #[rustler::nif] 659 | fn engine_fail_on_invalid_map_property(resource: ResourceArc) -> bool { 660 | let engine = resource.engine.try_lock().unwrap(); 661 | 662 | engine.fail_on_invalid_map_property() 663 | } 664 | 665 | #[rustler::nif] 666 | fn engine_set_fast_operators(resource: ResourceArc, enable: bool) { 667 | let mut engine = resource.engine.try_lock().unwrap(); 668 | 669 | engine.set_fast_operators(enable); 670 | } 671 | 672 | #[rustler::nif] 673 | fn engine_fast_operators(resource: ResourceArc) -> bool { 674 | let engine = resource.engine.try_lock().unwrap(); 675 | 676 | engine.fast_operators() 677 | } 678 | 679 | #[rustler::nif] 680 | fn engine_set_max_array_size(resource: ResourceArc, max_size: usize) { 681 | let mut engine = resource.engine.try_lock().unwrap(); 682 | 683 | engine.set_max_array_size(max_size); 684 | } 685 | 686 | #[rustler::nif] 687 | fn engine_max_array_size(resource: ResourceArc) -> usize { 688 | let engine = resource.engine.try_lock().unwrap(); 689 | 690 | engine.max_array_size() 691 | } 692 | 693 | #[rustler::nif] 694 | fn engine_set_max_call_levels(resource: ResourceArc, levels: usize) { 695 | let mut engine = resource.engine.try_lock().unwrap(); 696 | 697 | engine.set_max_call_levels(levels); 698 | } 699 | 700 | #[rustler::nif] 701 | fn engine_max_call_levels(resource: ResourceArc) -> usize { 702 | let engine = resource.engine.try_lock().unwrap(); 703 | 704 | engine.max_call_levels() 705 | } 706 | 707 | #[rustler::nif] 708 | fn engine_set_max_expr_depths( 709 | resource: ResourceArc, 710 | max_expr_depth: usize, 711 | max_function_expr_depth: usize, 712 | ) { 713 | let mut engine = resource.engine.try_lock().unwrap(); 714 | 715 | engine.set_max_expr_depths(max_expr_depth, max_function_expr_depth); 716 | } 717 | 718 | #[rustler::nif] 719 | fn engine_max_expr_depth(resource: ResourceArc) -> usize { 720 | let engine = resource.engine.try_lock().unwrap(); 721 | 722 | engine.max_expr_depth() 723 | } 724 | 725 | #[rustler::nif] 726 | fn engine_max_function_expr_depth(resource: ResourceArc) -> usize { 727 | let engine = resource.engine.try_lock().unwrap(); 728 | 729 | engine.max_function_expr_depth() 730 | } 731 | 732 | #[rustler::nif] 733 | fn engine_set_max_map_size(resource: ResourceArc, max_size: usize) { 734 | let mut engine = resource.engine.try_lock().unwrap(); 735 | 736 | engine.set_max_map_size(max_size); 737 | } 738 | 739 | #[rustler::nif] 740 | fn engine_max_map_size(resource: ResourceArc) -> usize { 741 | let engine = resource.engine.try_lock().unwrap(); 742 | 743 | engine.max_map_size() 744 | } 745 | 746 | #[rustler::nif] 747 | fn engine_set_max_modules(resource: ResourceArc, modules: usize) { 748 | let mut engine = resource.engine.try_lock().unwrap(); 749 | 750 | engine.set_max_modules(modules); 751 | } 752 | 753 | #[rustler::nif] 754 | fn engine_max_modules(resource: ResourceArc) -> usize { 755 | let engine = resource.engine.try_lock().unwrap(); 756 | 757 | engine.max_modules() 758 | } 759 | 760 | #[rustler::nif] 761 | fn engine_set_max_operations(resource: ResourceArc, operations: u64) { 762 | let mut engine = resource.engine.try_lock().unwrap(); 763 | 764 | engine.set_max_operations(operations); 765 | } 766 | 767 | #[rustler::nif] 768 | fn engine_max_operations(resource: ResourceArc) -> u64 { 769 | let engine = resource.engine.try_lock().unwrap(); 770 | 771 | engine.max_operations() 772 | } 773 | 774 | #[rustler::nif] 775 | fn engine_set_max_string_size(resource: ResourceArc, max_len: usize) { 776 | let mut engine = resource.engine.try_lock().unwrap(); 777 | 778 | engine.set_max_string_size(max_len); 779 | } 780 | 781 | #[rustler::nif] 782 | fn engine_max_string_size(resource: ResourceArc) -> usize { 783 | let engine = resource.engine.try_lock().unwrap(); 784 | 785 | engine.max_string_size() 786 | } 787 | 788 | #[rustler::nif] 789 | fn engine_set_strict_variables(resource: ResourceArc, enable: bool) { 790 | let mut engine = resource.engine.try_lock().unwrap(); 791 | 792 | engine.set_strict_variables(enable); 793 | } 794 | 795 | #[rustler::nif] 796 | fn engine_strict_variables(resource: ResourceArc) -> bool { 797 | let engine = resource.engine.try_lock().unwrap(); 798 | 799 | engine.strict_variables() 800 | } 801 | 802 | #[derive(NifUnitEnum)] 803 | enum OptimizationLevel { 804 | None, 805 | Simple, 806 | Full, 807 | } 808 | 809 | impl From for OptimizationLevel { 810 | fn from(optimization_level: rhai::OptimizationLevel) -> Self { 811 | match optimization_level { 812 | rhai::OptimizationLevel::None => OptimizationLevel::None, 813 | rhai::OptimizationLevel::Simple => OptimizationLevel::Simple, 814 | rhai::OptimizationLevel::Full => OptimizationLevel::Full, 815 | // This is needed because rhai::OptimizationLevel is #[non_exhaustive] 816 | _ => todo!("OptimizationLevel not supported yet."), 817 | } 818 | } 819 | } 820 | 821 | impl From for rhai::OptimizationLevel { 822 | fn from(optimization_level: OptimizationLevel) -> Self { 823 | match optimization_level { 824 | OptimizationLevel::None => rhai::OptimizationLevel::None, 825 | OptimizationLevel::Simple => rhai::OptimizationLevel::Simple, 826 | OptimizationLevel::Full => rhai::OptimizationLevel::Full, 827 | } 828 | } 829 | } 830 | 831 | #[rustler::nif] 832 | fn engine_optimization_level(resource: ResourceArc) -> OptimizationLevel { 833 | let engine = resource.engine.try_lock().unwrap(); 834 | 835 | engine.optimization_level().into() 836 | } 837 | 838 | #[rustler::nif] 839 | fn engine_set_optimization_level( 840 | resource: ResourceArc, 841 | optimization_level: OptimizationLevel, 842 | ) { 843 | let mut engine = resource.engine.try_lock().unwrap(); 844 | 845 | engine.set_optimization_level(optimization_level.into()); 846 | } 847 | #[rustler::nif] 848 | fn engine_optimize_ast( 849 | resource: ResourceArc, 850 | scope_resource: ResourceArc, 851 | ast_resource: ResourceArc, 852 | optimization_level: OptimizationLevel, 853 | ) -> ResourceArc { 854 | let engine = resource.engine.try_lock().unwrap(); 855 | let scope = scope_resource.scope.try_lock().unwrap(); 856 | let ast = ast_resource.ast.try_lock().unwrap().clone(); 857 | 858 | let result = engine.optimize_ast(&scope, ast, optimization_level.into()); 859 | 860 | ResourceArc::new(ASTResource { 861 | ast: Mutex::new(result), 862 | }) 863 | } 864 | 865 | #[rustler::nif] 866 | fn engine_disable_symbol(resource: ResourceArc, symbol: &str) { 867 | let mut engine = resource.engine.try_lock().unwrap(); 868 | 869 | engine.disable_symbol(symbol); 870 | } 871 | 872 | #[rustler::nif] 873 | fn engine_ensure_data_size_within_limits<'a>( 874 | env: Env<'a>, 875 | resource: ResourceArc, 876 | value: Term<'a>, 877 | ) -> Result<(), RhaiRustlerError> { 878 | let engine = resource.engine.try_lock().unwrap(); 879 | engine.ensure_data_size_within_limits(&to_dynamic(env, &value))?; 880 | 881 | Ok(()) 882 | } 883 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v1.2.0](https://github.com/rhaiscript/rhai_rustler/tree/v1.2.0) (2025-02-25) 4 | 5 | [Full Changelog](https://github.com/rhaiscript/rhai_rustler/compare/v1.1.1...v1.2.0) 6 | 7 | - build\(deps\): bump native rust deps [\#265](https://github.com/rhaiscript/rhai_rustler/pull/265) ([fabriziosestito](https://github.com/fabriziosestito)) 8 | - build\(deps\): bump rhai-dylib from 0.5.0 to 0.6.0 in /native/test\_dylib\_module [\#264](https://github.com/rhaiscript/rhai_rustler/pull/264) ([dependabot[bot]](https://github.com/apps/dependabot)) 9 | - build\(deps-dev\): bump ex\_doc from 0.36.1 to 0.37.2 [\#263](https://github.com/rhaiscript/rhai_rustler/pull/263) ([dependabot[bot]](https://github.com/apps/dependabot)) 10 | - build\(deps-dev\): bump stream\_data from 1.1.2 to 1.1.3 [\#261](https://github.com/rhaiscript/rhai_rustler/pull/261) ([dependabot[bot]](https://github.com/apps/dependabot)) 11 | - build\(deps\): bump rustler from 0.36.0 to 0.36.1 in /native/rhai\_rustler [\#259](https://github.com/rhaiscript/rhai_rustler/pull/259) ([dependabot[bot]](https://github.com/apps/dependabot)) 12 | - build\(deps\): bump rustler from 0.36.0 to 0.36.1 [\#258](https://github.com/rhaiscript/rhai_rustler/pull/258) ([dependabot[bot]](https://github.com/apps/dependabot)) 13 | - build\(deps\): bump rustler from 0.36.0 to 0.36.1 in /native/test\_dylib\_module [\#257](https://github.com/rhaiscript/rhai_rustler/pull/257) ([dependabot[bot]](https://github.com/apps/dependabot)) 14 | - build\(deps\): bump rhai from 1.20.1 to 1.21.0 in /native/rhai\_rustler [\#256](https://github.com/rhaiscript/rhai_rustler/pull/256) ([dependabot[bot]](https://github.com/apps/dependabot)) 15 | - build\(deps\): bump rustler from 0.35.0 to 0.36.0 in /native/test\_dylib\_module [\#255](https://github.com/rhaiscript/rhai_rustler/pull/255) ([dependabot[bot]](https://github.com/apps/dependabot)) 16 | - build\(deps\): bump rustler from 0.35.0 to 0.36.0 in /native/rhai\_rustler [\#254](https://github.com/rhaiscript/rhai_rustler/pull/254) ([dependabot[bot]](https://github.com/apps/dependabot)) 17 | - build\(deps\): bump rustler from 0.35.1 to 0.36.0 [\#253](https://github.com/rhaiscript/rhai_rustler/pull/253) ([dependabot[bot]](https://github.com/apps/dependabot)) 18 | - build\(deps\): bump thiserror from 2.0.9 to 2.0.11 in /native/rhai\_rustler [\#252](https://github.com/rhaiscript/rhai_rustler/pull/252) ([dependabot[bot]](https://github.com/apps/dependabot)) 19 | - build\(deps\): bump rhai-dylib from 0.4.0 to 0.5.0 in /native/rhai\_rustler [\#250](https://github.com/rhaiscript/rhai_rustler/pull/250) ([dependabot[bot]](https://github.com/apps/dependabot)) 20 | - ci: change macos version to latest [\#249](https://github.com/rhaiscript/rhai_rustler/pull/249) ([fabriziosestito](https://github.com/fabriziosestito)) 21 | - build\(deps-dev\): bump credo from 1.7.10 to 1.7.11 [\#248](https://github.com/rhaiscript/rhai_rustler/pull/248) ([dependabot[bot]](https://github.com/apps/dependabot)) 22 | - build\(deps\): bump rhai-dylib from 0.4.0 to 0.5.0 in /native/test\_dylib\_module [\#247](https://github.com/rhaiscript/rhai_rustler/pull/247) ([dependabot[bot]](https://github.com/apps/dependabot)) 23 | - build\(deps-dev\): bump ex\_doc from 0.35.1 to 0.36.1 [\#246](https://github.com/rhaiscript/rhai_rustler/pull/246) ([dependabot[bot]](https://github.com/apps/dependabot)) 24 | - build\(deps\): bump thiserror from 1.0.65 to 2.0.9 in /native/rhai\_rustler [\#244](https://github.com/rhaiscript/rhai_rustler/pull/244) ([dependabot[bot]](https://github.com/apps/dependabot)) 25 | - ci: bump macos version to 12 [\#243](https://github.com/rhaiscript/rhai_rustler/pull/243) ([fabriziosestito](https://github.com/fabriziosestito)) 26 | - fix: resource impl [\#242](https://github.com/rhaiscript/rhai_rustler/pull/242) ([fabriziosestito](https://github.com/fabriziosestito)) 27 | - build\(deps\): bump rustler from 0.35.0 to 0.35.1 [\#238](https://github.com/rhaiscript/rhai_rustler/pull/238) ([dependabot[bot]](https://github.com/apps/dependabot)) 28 | - build\(deps\): bump rhai from 1.19.0 to 1.20.1 in /native/rhai\_rustler [\#236](https://github.com/rhaiscript/rhai_rustler/pull/236) ([dependabot[bot]](https://github.com/apps/dependabot)) 29 | - build\(deps-dev\): bump ex\_doc from 0.34.2 to 0.35.1 [\#233](https://github.com/rhaiscript/rhai_rustler/pull/233) ([dependabot[bot]](https://github.com/apps/dependabot)) 30 | - build\(deps-dev\): bump dialyxir from 1.4.4 to 1.4.5 [\#231](https://github.com/rhaiscript/rhai_rustler/pull/231) ([dependabot[bot]](https://github.com/apps/dependabot)) 31 | - build\(deps-dev\): bump credo from 1.7.9 to 1.7.10 [\#228](https://github.com/rhaiscript/rhai_rustler/pull/228) ([dependabot[bot]](https://github.com/apps/dependabot)) 32 | - build\(deps-dev\): bump credo from 1.7.8 to 1.7.9 [\#224](https://github.com/rhaiscript/rhai_rustler/pull/224) ([dependabot[bot]](https://github.com/apps/dependabot)) 33 | - build\(deps\): bump thiserror from 1.0.64 to 1.0.65 in /native/rhai\_rustler [\#222](https://github.com/rhaiscript/rhai_rustler/pull/222) ([dependabot[bot]](https://github.com/apps/dependabot)) 34 | - fix: rustler update to v0.35.0 [\#221](https://github.com/rhaiscript/rhai_rustler/pull/221) ([fabriziosestito](https://github.com/fabriziosestito)) 35 | - build\(deps\): bump rhai-dylib from 0.3.0 to 0.4.0 in /native/rhai\_rustler [\#220](https://github.com/rhaiscript/rhai_rustler/pull/220) ([dependabot[bot]](https://github.com/apps/dependabot)) 36 | - build\(deps\): bump rustler from 0.34.0 to 0.35.0 in /native/test\_dylib\_module [\#219](https://github.com/rhaiscript/rhai_rustler/pull/219) ([dependabot[bot]](https://github.com/apps/dependabot)) 37 | - build\(deps\): bump rustler\_precompiled from 0.8.0 to 0.8.2 [\#218](https://github.com/rhaiscript/rhai_rustler/pull/218) ([dependabot[bot]](https://github.com/apps/dependabot)) 38 | - build\(deps-dev\): bump dialyxir from 1.4.3 to 1.4.4 [\#217](https://github.com/rhaiscript/rhai_rustler/pull/217) ([dependabot[bot]](https://github.com/apps/dependabot)) 39 | - build\(deps\): bump rustler from 0.33.0 to 0.35.0 in /native/rhai\_rustler [\#216](https://github.com/rhaiscript/rhai_rustler/pull/216) ([dependabot[bot]](https://github.com/apps/dependabot)) 40 | - build\(deps-dev\): bump stream\_data from 1.1.0 to 1.1.2 [\#215](https://github.com/rhaiscript/rhai_rustler/pull/215) ([dependabot[bot]](https://github.com/apps/dependabot)) 41 | - build\(deps-dev\): bump credo from 1.7.6 to 1.7.8 [\#214](https://github.com/rhaiscript/rhai_rustler/pull/214) ([dependabot[bot]](https://github.com/apps/dependabot)) 42 | - build\(deps\): bump rustler from 0.33.0 to 0.35.0 [\#213](https://github.com/rhaiscript/rhai_rustler/pull/213) ([dependabot[bot]](https://github.com/apps/dependabot)) 43 | - build\(deps\): bump thiserror from 1.0.63 to 1.0.64 in /native/rhai\_rustler [\#212](https://github.com/rhaiscript/rhai_rustler/pull/212) ([dependabot[bot]](https://github.com/apps/dependabot)) 44 | - build\(deps\): bump rustler\_precompiled from 0.7.1 to 0.8.0 [\#211](https://github.com/rhaiscript/rhai_rustler/pull/211) ([dependabot[bot]](https://github.com/apps/dependabot)) 45 | - build\(deps\): bump thiserror from 1.0.60 to 1.0.63 in /native/rhai\_rustler [\#210](https://github.com/rhaiscript/rhai_rustler/pull/210) ([dependabot[bot]](https://github.com/apps/dependabot)) 46 | - build\(deps\): bump rustler from 0.33.0 to 0.34.0 in /native/test\_dylib\_module [\#206](https://github.com/rhaiscript/rhai_rustler/pull/206) ([dependabot[bot]](https://github.com/apps/dependabot)) 47 | - build\(deps-dev\): bump ex\_doc from 0.34.0 to 0.34.2 [\#205](https://github.com/rhaiscript/rhai_rustler/pull/205) ([dependabot[bot]](https://github.com/apps/dependabot)) 48 | - build\(deps\): bump rhai-dylib from 0.3.0 to 0.4.0 in /native/test\_dylib\_module [\#204](https://github.com/rhaiscript/rhai_rustler/pull/204) ([dependabot[bot]](https://github.com/apps/dependabot)) 49 | - build\(deps\): bump rhai from 1.18.0 to 1.19.0 in /native/rhai\_rustler [\#203](https://github.com/rhaiscript/rhai_rustler/pull/203) ([dependabot[bot]](https://github.com/apps/dependabot)) 50 | - build\(deps-dev\): bump ex\_doc from 0.32.2 to 0.34.0 [\#198](https://github.com/rhaiscript/rhai_rustler/pull/198) ([dependabot[bot]](https://github.com/apps/dependabot)) 51 | - build\(deps\): bump rustler from 0.32.1 to 0.33.0 [\#197](https://github.com/rhaiscript/rhai_rustler/pull/197) ([dependabot[bot]](https://github.com/apps/dependabot)) 52 | - build\(deps\): bump rustler from 0.32.1 to 0.33.0 in /native/test\_dylib\_module [\#196](https://github.com/rhaiscript/rhai_rustler/pull/196) ([dependabot[bot]](https://github.com/apps/dependabot)) 53 | - build\(deps\): bump rustler from 0.32.1 to 0.33.0 in /native/rhai\_rustler [\#195](https://github.com/rhaiscript/rhai_rustler/pull/195) ([dependabot[bot]](https://github.com/apps/dependabot)) 54 | - build\(deps-dev\): bump stream\_data from 1.0.0 to 1.1.0 [\#194](https://github.com/rhaiscript/rhai_rustler/pull/194) ([dependabot[bot]](https://github.com/apps/dependabot)) 55 | - build\(deps\): bump philss/rustler-precompiled-action from 1.1.3 to 1.1.4 [\#191](https://github.com/rhaiscript/rhai_rustler/pull/191) ([dependabot[bot]](https://github.com/apps/dependabot)) 56 | - build\(deps\): bump rhai-dylib from 0.2.1 to 0.3.0 in /native/rhai\_rustler [\#190](https://github.com/rhaiscript/rhai_rustler/pull/190) ([dependabot[bot]](https://github.com/apps/dependabot)) 57 | - ci: use stable toolchain when running tests [\#189](https://github.com/rhaiscript/rhai_rustler/pull/189) ([fabriziosestito](https://github.com/fabriziosestito)) 58 | - build\(deps-dev\): bump stream\_data from 0.6.0 to 1.0.0 [\#188](https://github.com/rhaiscript/rhai_rustler/pull/188) ([dependabot[bot]](https://github.com/apps/dependabot)) 59 | - build\(deps-dev\): bump ex\_doc from 0.31.2 to 0.32.2 [\#187](https://github.com/rhaiscript/rhai_rustler/pull/187) ([dependabot[bot]](https://github.com/apps/dependabot)) 60 | - build\(deps-dev\): bump credo from 1.7.5 to 1.7.6 [\#186](https://github.com/rhaiscript/rhai_rustler/pull/186) ([dependabot[bot]](https://github.com/apps/dependabot)) 61 | - build\(deps\): bump thiserror from 1.0.58 to 1.0.60 in /native/rhai\_rustler [\#185](https://github.com/rhaiscript/rhai_rustler/pull/185) ([dependabot[bot]](https://github.com/apps/dependabot)) 62 | - build\(deps\): bump rhai from 1.17.1 to 1.18.0 in /native/rhai\_rustler [\#182](https://github.com/rhaiscript/rhai_rustler/pull/182) ([dependabot[bot]](https://github.com/apps/dependabot)) 63 | - build\(deps\): bump rhai-dylib from 0.2.1 to 0.3.0 in /native/test\_dylib\_module [\#181](https://github.com/rhaiscript/rhai_rustler/pull/181) ([dependabot[bot]](https://github.com/apps/dependabot)) 64 | - build\(deps\): bump rustler from 0.31.0 to 0.32.1 in /native/test\_dylib\_module [\#179](https://github.com/rhaiscript/rhai_rustler/pull/179) ([dependabot[bot]](https://github.com/apps/dependabot)) 65 | - build\(deps\): bump rustler from 0.31.0 to 0.32.1 [\#178](https://github.com/rhaiscript/rhai_rustler/pull/178) ([dependabot[bot]](https://github.com/apps/dependabot)) 66 | - build\(deps\): bump rustler from 0.31.0 to 0.32.1 in /native/rhai\_rustler [\#177](https://github.com/rhaiscript/rhai_rustler/pull/177) ([dependabot[bot]](https://github.com/apps/dependabot)) 67 | 68 | ## [v1.1.1](https://github.com/rhaiscript/rhai_rustler/tree/v1.1.1) (2024-03-12) 69 | 70 | [Full Changelog](https://github.com/rhaiscript/rhai_rustler/compare/v1.1.0...v1.1.1) 71 | 72 | - Contribute to rhai org? [\#168](https://github.com/rhaiscript/rhai_rustler/issues/168) 73 | 74 | - build\(deps\): bump rust dependencies [\#176](https://github.com/rhaiscript/rhai_rustler/pull/176) ([fabriziosestito](https://github.com/fabriziosestito)) 75 | - build\(deps\): bump softprops/action-gh-release from 1 to 2 [\#175](https://github.com/rhaiscript/rhai_rustler/pull/175) ([dependabot[bot]](https://github.com/apps/dependabot)) 76 | - build\(deps-dev\): bump ex\_doc from 0.31.1 to 0.31.2 [\#174](https://github.com/rhaiscript/rhai_rustler/pull/174) ([dependabot[bot]](https://github.com/apps/dependabot)) 77 | - build\(deps-dev\): bump credo from 1.7.4 to 1.7.5 [\#173](https://github.com/rhaiscript/rhai_rustler/pull/173) ([dependabot[bot]](https://github.com/apps/dependabot)) 78 | - build\(deps\): bump rustler from 0.30.0 to 0.31.0 [\#172](https://github.com/rhaiscript/rhai_rustler/pull/172) ([dependabot[bot]](https://github.com/apps/dependabot)) 79 | - build\(deps\): bump rustler from 0.30.0 to 0.31.0 in /native/test\_dylib\_module [\#171](https://github.com/rhaiscript/rhai_rustler/pull/171) ([dependabot[bot]](https://github.com/apps/dependabot)) 80 | - build\(deps-dev\): bump credo from 1.7.3 to 1.7.4 [\#167](https://github.com/rhaiscript/rhai_rustler/pull/167) ([dependabot[bot]](https://github.com/apps/dependabot)) 81 | - build\(deps\): bump rhai-dylib from 0.2.0 to 0.2.1 in /native/test\_dylib\_module [\#166](https://github.com/rhaiscript/rhai_rustler/pull/166) ([dependabot[bot]](https://github.com/apps/dependabot)) 82 | - build\(deps\): bump rhai from 1.16.3 to 1.17.1 in /native/rhai\_rustler [\#164](https://github.com/rhaiscript/rhai_rustler/pull/164) ([dependabot[bot]](https://github.com/apps/dependabot)) 83 | - build\(deps\): bump rhai-dylib from 0.1.12 to 0.2.0 in /native/test\_dylib\_module [\#163](https://github.com/rhaiscript/rhai_rustler/pull/163) ([dependabot[bot]](https://github.com/apps/dependabot)) 84 | - build\(deps\): bump actions/cache from 3 to 4 [\#161](https://github.com/rhaiscript/rhai_rustler/pull/161) ([dependabot[bot]](https://github.com/apps/dependabot)) 85 | - Bump ex\_doc from 0.30.9 to 0.31.1 [\#160](https://github.com/rhaiscript/rhai_rustler/pull/160) ([dependabot[bot]](https://github.com/apps/dependabot)) 86 | - Bump credo from 1.7.1 to 1.7.3 [\#159](https://github.com/rhaiscript/rhai_rustler/pull/159) ([dependabot[bot]](https://github.com/apps/dependabot)) 87 | - Bump thiserror from 1.0.50 to 1.0.56 in /native/rhai\_rustler [\#158](https://github.com/rhaiscript/rhai_rustler/pull/158) ([dependabot[bot]](https://github.com/apps/dependabot)) 88 | - Bump dialyxir from 1.4.2 to 1.4.3 [\#156](https://github.com/rhaiscript/rhai_rustler/pull/156) ([dependabot[bot]](https://github.com/apps/dependabot)) 89 | - Bump actions/upload-artifact from 3 to 4 [\#152](https://github.com/rhaiscript/rhai_rustler/pull/152) ([dependabot[bot]](https://github.com/apps/dependabot)) 90 | - Bump rustler\_precompiled from 0.7.0 to 0.7.1 [\#150](https://github.com/rhaiscript/rhai_rustler/pull/150) ([dependabot[bot]](https://github.com/apps/dependabot)) 91 | 92 | ## [v1.1.0](https://github.com/rhaiscript/rhai_rustler/tree/v1.1.0) (2023-11-13) 93 | 94 | [Full Changelog](https://github.com/rhaiscript/rhai_rustler/compare/v1.0.2...v1.1.0) 95 | 96 | - fix: integer float conversion and native deps bump [\#149](https://github.com/rhaiscript/rhai_rustler/pull/149) ([fabriziosestito](https://github.com/fabriziosestito)) 97 | - Bump philss/rustler-precompiled-action from 1.1.1 to 1.1.3 [\#147](https://github.com/rhaiscript/rhai_rustler/pull/147) ([dependabot[bot]](https://github.com/apps/dependabot)) 98 | - Bump dialyxir from 1.3.0 to 1.4.2 [\#146](https://github.com/rhaiscript/rhai_rustler/pull/146) ([dependabot[bot]](https://github.com/apps/dependabot)) 99 | - Bump thiserror from 1.0.49 to 1.0.50 in /native/rhai\_rustler [\#145](https://github.com/rhaiscript/rhai_rustler/pull/145) ([dependabot[bot]](https://github.com/apps/dependabot)) 100 | - Bump ex\_doc from 0.30.8 to 0.30.9 [\#144](https://github.com/rhaiscript/rhai_rustler/pull/144) ([dependabot[bot]](https://github.com/apps/dependabot)) 101 | - Bump philss/rustler-precompiled-action from 1.1.0 to 1.1.1 [\#143](https://github.com/rhaiscript/rhai_rustler/pull/143) ([dependabot[bot]](https://github.com/apps/dependabot)) 102 | - Bump ex\_doc from 0.30.6 to 0.30.8 [\#142](https://github.com/rhaiscript/rhai_rustler/pull/142) ([dependabot[bot]](https://github.com/apps/dependabot)) 103 | - Bump rustler from 0.29.1 to 0.30.0 in /native/rhai\_rustler [\#140](https://github.com/rhaiscript/rhai_rustler/pull/140) ([dependabot[bot]](https://github.com/apps/dependabot)) 104 | - Bump rustler from 0.29.1 to 0.30.0 in /native/test\_dylib\_module [\#139](https://github.com/rhaiscript/rhai_rustler/pull/139) ([dependabot[bot]](https://github.com/apps/dependabot)) 105 | - Bump rustler from 0.29.1 to 0.30.0 [\#138](https://github.com/rhaiscript/rhai_rustler/pull/138) ([dependabot[bot]](https://github.com/apps/dependabot)) 106 | - Bump credo from 1.7.0 to 1.7.1 [\#137](https://github.com/rhaiscript/rhai_rustler/pull/137) ([dependabot[bot]](https://github.com/apps/dependabot)) 107 | - Bump thiserror from 1.0.44 to 1.0.49 in /native/rhai\_rustler [\#136](https://github.com/rhaiscript/rhai_rustler/pull/136) ([dependabot[bot]](https://github.com/apps/dependabot)) 108 | - Bump philss/rustler-precompiled-action from 1.0.1 to 1.1.0 [\#135](https://github.com/rhaiscript/rhai_rustler/pull/135) ([dependabot[bot]](https://github.com/apps/dependabot)) 109 | - Bump rustler\_precompiled from 0.6.3 to 0.7.0 [\#134](https://github.com/rhaiscript/rhai_rustler/pull/134) ([dependabot[bot]](https://github.com/apps/dependabot)) 110 | - Bump rhai-dylib from 0.1.9 to 0.1.11 in /native/test\_dylib\_module [\#133](https://github.com/rhaiscript/rhai_rustler/pull/133) ([dependabot[bot]](https://github.com/apps/dependabot)) 111 | - Bump actions/checkout from 3 to 4 [\#130](https://github.com/rhaiscript/rhai_rustler/pull/130) ([dependabot[bot]](https://github.com/apps/dependabot)) 112 | - Bump rustler\_precompiled from 0.6.2 to 0.6.3 [\#128](https://github.com/rhaiscript/rhai_rustler/pull/128) ([dependabot[bot]](https://github.com/apps/dependabot)) 113 | - Bump ex\_doc from 0.30.5 to 0.30.6 [\#126](https://github.com/rhaiscript/rhai_rustler/pull/126) ([dependabot[bot]](https://github.com/apps/dependabot)) 114 | - Bump ex\_doc from 0.30.3 to 0.30.5 [\#122](https://github.com/rhaiscript/rhai_rustler/pull/122) ([dependabot[bot]](https://github.com/apps/dependabot)) 115 | 116 | ## [v1.0.2](https://github.com/rhaiscript/rhai_rustler/tree/v1.0.2) (2023-07-28) 117 | 118 | [Full Changelog](https://github.com/rhaiscript/rhai_rustler/compare/v1.0.1...v1.0.2) 119 | 120 | - Bump thiserror from 1.0.43 to 1.0.44 in /native/rhai\_rustler [\#120](https://github.com/rhaiscript/rhai_rustler/pull/120) ([dependabot[bot]](https://github.com/apps/dependabot)) 121 | - Bump ex\_doc from 0.30.2 to 0.30.3 [\#119](https://github.com/rhaiscript/rhai_rustler/pull/119) ([dependabot[bot]](https://github.com/apps/dependabot)) 122 | 123 | ## [v1.0.1](https://github.com/rhaiscript/rhai_rustler/tree/v1.0.1) (2023-07-17) 124 | 125 | [Full Changelog](https://github.com/rhaiscript/rhai_rustler/compare/v1.0.0...v1.0.1) 126 | 127 | - fix: use dirty cpu scheduler for compilation related NIFs [\#117](https://github.com/rhaiscript/rhai_rustler/pull/117) ([fabriziosestito](https://github.com/fabriziosestito)) 128 | - fix: use dirty cpu scheduler for evaluation functions [\#116](https://github.com/rhaiscript/rhai_rustler/pull/116) ([fabriziosestito](https://github.com/fabriziosestito)) 129 | - Bump ex\_doc from 0.29.4 to 0.30.2 [\#115](https://github.com/rhaiscript/rhai_rustler/pull/115) ([dependabot[bot]](https://github.com/apps/dependabot)) 130 | - Bump stream\_data from 0.5.0 to 0.6.0 [\#113](https://github.com/rhaiscript/rhai_rustler/pull/113) ([dependabot[bot]](https://github.com/apps/dependabot)) 131 | - Bump thiserror from 1.0.40 to 1.0.43 in /native/rhai\_rustler [\#111](https://github.com/rhaiscript/rhai_rustler/pull/111) ([dependabot[bot]](https://github.com/apps/dependabot)) 132 | - Bump rustler\_precompiled from 0.6.1 to 0.6.2 [\#110](https://github.com/rhaiscript/rhai_rustler/pull/110) ([dependabot[bot]](https://github.com/apps/dependabot)) 133 | - Bump rustler from 0.29.0 to 0.29.1 in /native/test\_dylib\_module [\#108](https://github.com/rhaiscript/rhai_rustler/pull/108) ([dependabot[bot]](https://github.com/apps/dependabot)) 134 | - Bump rustler from 0.29.0 to 0.29.1 [\#107](https://github.com/rhaiscript/rhai_rustler/pull/107) ([dependabot[bot]](https://github.com/apps/dependabot)) 135 | - build\(deps\): bump rhai\_dylib to 0.1.9 [\#106](https://github.com/rhaiscript/rhai_rustler/pull/106) ([fabriziosestito](https://github.com/fabriziosestito)) 136 | - Bump rhai-dylib from 0.1.8 to 0.1.9 in /native/test\_dylib\_module [\#105](https://github.com/rhaiscript/rhai_rustler/pull/105) ([dependabot[bot]](https://github.com/apps/dependabot)) 137 | - Bump rustler from 0.28.0 to 0.29.0 in /native/test\_dylib\_module [\#104](https://github.com/rhaiscript/rhai_rustler/pull/104) ([dependabot[bot]](https://github.com/apps/dependabot)) 138 | - Bump rustler from 0.28.0 to 0.29.0 [\#103](https://github.com/rhaiscript/rhai_rustler/pull/103) ([dependabot[bot]](https://github.com/apps/dependabot)) 139 | - Bump rustler from 0.28.0 to 0.29.0 in /native/rhai\_rustler [\#102](https://github.com/rhaiscript/rhai_rustler/pull/102) ([dependabot[bot]](https://github.com/apps/dependabot)) 140 | 141 | ## [v1.0.0](https://github.com/rhaiscript/rhai_rustler/tree/v1.0.0) (2023-05-20) 142 | 143 | [Full Changelog](https://github.com/rhaiscript/rhai_rustler/compare/v0.1.4...v1.0.0) 144 | 145 | - Rename/refactor Scope functions to be more ergonomic/idiomatic in Elixir [\#41](https://github.com/rhaiscript/rhai_rustler/issues/41) 146 | - Proper error handling [\#30](https://github.com/rhaiscript/rhai_rustler/issues/30) 147 | - Engine options bindings pt1 [\#36](https://github.com/rhaiscript/rhai_rustler/pull/36) ([dottorblaster](https://github.com/dottorblaster)) 148 | 149 | - Specs are wrong [\#66](https://github.com/rhaiscript/rhai_rustler/issues/66) 150 | 151 | - Test file moduleresolver [\#80](https://github.com/rhaiscript/rhai_rustler/issues/80) 152 | - Clean-up [\#55](https://github.com/rhaiscript/rhai_rustler/issues/55) 153 | - Test Scope set\_alias [\#53](https://github.com/rhaiscript/rhai_rustler/issues/53) 154 | - Add dylib example [\#43](https://github.com/rhaiscript/rhai_rustler/issues/43) 155 | - AST bindings [\#34](https://github.com/rhaiscript/rhai_rustler/issues/34) 156 | - Scope bindings [\#33](https://github.com/rhaiscript/rhai_rustler/issues/33) 157 | - Engine bindings [\#32](https://github.com/rhaiscript/rhai_rustler/issues/32) 158 | 159 | - Bump rhai to v1.14.0 and rhai\_dylib to v0.1.8 [\#100](https://github.com/rhaiscript/rhai_rustler/pull/100) ([fabriziosestito](https://github.com/fabriziosestito)) 160 | - Add test dylib module to dependabot [\#97](https://github.com/rhaiscript/rhai_rustler/pull/97) ([fabriziosestito](https://github.com/fabriziosestito)) 161 | - Bump rustler from 0.27.0 to 0.28.0 [\#95](https://github.com/rhaiscript/rhai_rustler/pull/95) ([dependabot[bot]](https://github.com/apps/dependabot)) 162 | - Bump rustler from 0.27.0 to 0.28.0 in /native/rhai\_rustler [\#94](https://github.com/rhaiscript/rhai_rustler/pull/94) ([dependabot[bot]](https://github.com/apps/dependabot)) 163 | - Test FileModuleResolver [\#93](https://github.com/rhaiscript/rhai_rustler/pull/93) ([fabriziosestito](https://github.com/fabriziosestito)) 164 | - Refactor/rename Scope functions [\#92](https://github.com/rhaiscript/rhai_rustler/pull/92) ([fabriziosestito](https://github.com/fabriziosestito)) 165 | - Add register package [\#91](https://github.com/rhaiscript/rhai_rustler/pull/91) ([fabriziosestito](https://github.com/fabriziosestito)) 166 | - Refactor module resolvers and tests [\#90](https://github.com/rhaiscript/rhai_rustler/pull/90) ([fabriziosestito](https://github.com/fabriziosestito)) 167 | - Remove Scope set\_alias [\#88](https://github.com/rhaiscript/rhai_rustler/pull/88) ([fabriziosestito](https://github.com/fabriziosestito)) 168 | - Bump rhai\_dylib to 0.1.7 [\#87](https://github.com/rhaiscript/rhai_rustler/pull/87) ([fabriziosestito](https://github.com/fabriziosestito)) 169 | - Bind Engine enusre\_data\_size\_within\_limits [\#86](https://github.com/rhaiscript/rhai_rustler/pull/86) ([fabriziosestito](https://github.com/fabriziosestito)) 170 | - Use from\_array [\#85](https://github.com/rhaiscript/rhai_rustler/pull/85) ([fabriziosestito](https://github.com/fabriziosestito)) 171 | - Bind Engine register\_custom\_operator [\#84](https://github.com/rhaiscript/rhai_rustler/pull/84) ([fabriziosestito](https://github.com/fabriziosestito)) 172 | - Bind Engine.disable\_symbol/2 [\#83](https://github.com/rhaiscript/rhai_rustler/pull/83) ([fabriziosestito](https://github.com/fabriziosestito)) 173 | - Bind Engine AST optimization methods [\#82](https://github.com/rhaiscript/rhai_rustler/pull/82) ([fabriziosestito](https://github.com/fabriziosestito)) 174 | - Bind Engine::new\_raw\(\) [\#81](https://github.com/rhaiscript/rhai_rustler/pull/81) ([fabriziosestito](https://github.com/fabriziosestito)) 175 | - Bind Engine::eval\* methods [\#79](https://github.com/rhaiscript/rhai_rustler/pull/79) ([fabriziosestito](https://github.com/fabriziosestito)) 176 | - Bind Engine::run\* methods [\#78](https://github.com/rhaiscript/rhai_rustler/pull/78) ([fabriziosestito](https://github.com/fabriziosestito)) 177 | - Bind Engine::compile\* methods [\#77](https://github.com/rhaiscript/rhai_rustler/pull/77) ([fabriziosestito](https://github.com/fabriziosestito)) 178 | - Bind Engine::compact\_script [\#76](https://github.com/rhaiscript/rhai_rustler/pull/76) ([fabriziosestito](https://github.com/fabriziosestito)) 179 | - Bind Engine::call\_fn [\#75](https://github.com/rhaiscript/rhai_rustler/pull/75) ([fabriziosestito](https://github.com/fabriziosestito)) 180 | - Bind AST functions and statements methods [\#74](https://github.com/rhaiscript/rhai_rustler/pull/74) ([fabriziosestito](https://github.com/fabriziosestito)) 181 | - Bump ex\_doc from 0.29.3 to 0.29.4 [\#72](https://github.com/rhaiscript/rhai_rustler/pull/72) ([dependabot[bot]](https://github.com/apps/dependabot)) 182 | - Bind AST::clear\_functions [\#71](https://github.com/rhaiscript/rhai_rustler/pull/71) ([fabriziosestito](https://github.com/fabriziosestito)) 183 | - Bind AST source-related methods [\#69](https://github.com/rhaiscript/rhai_rustler/pull/69) ([fabriziosestito](https://github.com/fabriziosestito)) 184 | - macOS extension workaround [\#68](https://github.com/rhaiscript/rhai_rustler/pull/68) ([fabriziosestito](https://github.com/fabriziosestito)) 185 | - Fix allow loop expressions test [\#67](https://github.com/rhaiscript/rhai_rustler/pull/67) ([fabriziosestito](https://github.com/fabriziosestito)) 186 | - Bump rhai from 1.12.0 to 1.13.0 in /native/rhai\_rustler [\#60](https://github.com/rhaiscript/rhai_rustler/pull/60) ([dependabot[bot]](https://github.com/apps/dependabot)) 187 | - Bump ex\_doc from 0.29.2 to 0.29.3 [\#59](https://github.com/rhaiscript/rhai_rustler/pull/59) ([dependabot[bot]](https://github.com/apps/dependabot)) 188 | - Bump thiserror from 1.0.38 to 1.0.39 in /native/rhai\_rustler [\#58](https://github.com/rhaiscript/rhai_rustler/pull/58) ([dependabot[bot]](https://github.com/apps/dependabot)) 189 | - Bump ex\_doc from 0.29.1 to 0.29.2 [\#57](https://github.com/rhaiscript/rhai_rustler/pull/57) ([dependabot[bot]](https://github.com/apps/dependabot)) 190 | - Bind Engine::run and Engine::run\_with\_scope [\#56](https://github.com/rhaiscript/rhai_rustler/pull/56) ([fabriziosestito](https://github.com/fabriziosestito)) 191 | - Bind Scope::set\_or\_push [\#54](https://github.com/rhaiscript/rhai_rustler/pull/54) ([fabriziosestito](https://github.com/fabriziosestito)) 192 | - Bind Scope::set\_alias [\#52](https://github.com/rhaiscript/rhai_rustler/pull/52) ([fabriziosestito](https://github.com/fabriziosestito)) 193 | - Bind Scope::pop and Scope::set\_value [\#51](https://github.com/rhaiscript/rhai_rustler/pull/51) ([fabriziosestito](https://github.com/fabriziosestito)) 194 | - Bind Scope::rewind [\#49](https://github.com/rhaiscript/rhai_rustler/pull/49) ([fabriziosestito](https://github.com/fabriziosestito)) 195 | - Bind Scope::remove [\#47](https://github.com/rhaiscript/rhai_rustler/pull/47) ([fabriziosestito](https://github.com/fabriziosestito)) 196 | - Bind Scope::with\_capacity [\#46](https://github.com/rhaiscript/rhai_rustler/pull/46) ([fabriziosestito](https://github.com/fabriziosestito)) 197 | - Bind Scope::len [\#45](https://github.com/rhaiscript/rhai_rustler/pull/45) ([fabriziosestito](https://github.com/fabriziosestito)) 198 | - Bind Scope::is\_empty [\#44](https://github.com/rhaiscript/rhai_rustler/pull/44) ([fabriziosestito](https://github.com/fabriziosestito)) 199 | - Bind Scope::clone\_visible [\#42](https://github.com/rhaiscript/rhai_rustler/pull/42) ([fabriziosestito](https://github.com/fabriziosestito)) 200 | - Bind get\_value instead of get [\#40](https://github.com/rhaiscript/rhai_rustler/pull/40) ([fabriziosestito](https://github.com/fabriziosestito)) 201 | - Add scope clear [\#39](https://github.com/rhaiscript/rhai_rustler/pull/39) ([fabriziosestito](https://github.com/fabriziosestito)) 202 | - Add credo template [\#38](https://github.com/rhaiscript/rhai_rustler/pull/38) ([fabriziosestito](https://github.com/fabriziosestito)) 203 | - Bump rustler\_precompiled from 0.6.0 to 0.6.1 [\#37](https://github.com/rhaiscript/rhai_rustler/pull/37) ([dependabot[bot]](https://github.com/apps/dependabot)) 204 | - Add scope bindings pt 1 [\#35](https://github.com/rhaiscript/rhai_rustler/pull/35) ([fabriziosestito](https://github.com/fabriziosestito)) 205 | - Refactor error handling [\#31](https://github.com/rhaiscript/rhai_rustler/pull/31) ([fabriziosestito](https://github.com/fabriziosestito)) 206 | - Try to wrap Engine compile function [\#29](https://github.com/rhaiscript/rhai_rustler/pull/29) ([dottorblaster](https://github.com/dottorblaster)) 207 | - Wrap engine pt1 [\#28](https://github.com/rhaiscript/rhai_rustler/pull/28) ([fabriziosestito](https://github.com/fabriziosestito)) 208 | - Bump rustler\_precompiled from 0.5.5 to 0.6.0 [\#26](https://github.com/rhaiscript/rhai_rustler/pull/26) ([dependabot[bot]](https://github.com/apps/dependabot)) 209 | - Bump giantswarm/install-binary-action from 1.0.0 to 1.1.0 [\#25](https://github.com/rhaiscript/rhai_rustler/pull/25) ([dependabot[bot]](https://github.com/apps/dependabot)) 210 | - Bump rustler from 0.26.0 to 0.27.0 in /native/rhai\_rustler [\#24](https://github.com/rhaiscript/rhai_rustler/pull/24) ([dependabot[bot]](https://github.com/apps/dependabot)) 211 | - Bump rustler from 0.26.0 to 0.27.0 [\#23](https://github.com/rhaiscript/rhai_rustler/pull/23) ([dependabot[bot]](https://github.com/apps/dependabot)) 212 | 213 | ## [v0.1.4](https://github.com/rhaiscript/rhai_rustler/tree/v0.1.4) (2023-01-16) 214 | 215 | [Full Changelog](https://github.com/rhaiscript/rhai_rustler/compare/v0.1.3...v0.1.4) 216 | 217 | - Use config to set force build [\#22](https://github.com/rhaiscript/rhai_rustler/pull/22) ([fabriziosestito](https://github.com/fabriziosestito)) 218 | - Pin ubuntu version in release workflow [\#21](https://github.com/rhaiscript/rhai_rustler/pull/21) ([fabriziosestito](https://github.com/fabriziosestito)) 219 | 220 | ## [v0.1.3](https://github.com/rhaiscript/rhai_rustler/tree/v0.1.3) (2023-01-03) 221 | 222 | [Full Changelog](https://github.com/rhaiscript/rhai_rustler/compare/v0.1.1...v0.1.3) 223 | 224 | - Bump to v0.1.3 [\#19](https://github.com/rhaiscript/rhai_rustler/pull/19) ([fabriziosestito](https://github.com/fabriziosestito)) 225 | - Bump rustler\_precompiled from 0.5.4 to 0.5.5 [\#12](https://github.com/rhaiscript/rhai_rustler/pull/12) ([dependabot[bot]](https://github.com/apps/dependabot)) 226 | - Bump ex\_doc from 0.29.0 to 0.29.1 [\#11](https://github.com/rhaiscript/rhai_rustler/pull/11) ([dependabot[bot]](https://github.com/apps/dependabot)) 227 | - Bump rhai from 1.10.1 to 1.11.0 in /native/rhai\_rustler [\#10](https://github.com/rhaiscript/rhai_rustler/pull/10) ([dependabot[bot]](https://github.com/apps/dependabot)) 228 | - Bump rustler\_precompiled from 0.5.3 to 0.5.4 [\#9](https://github.com/rhaiscript/rhai_rustler/pull/9) ([dependabot[bot]](https://github.com/apps/dependabot)) 229 | 230 | ## [v0.1.1](https://github.com/rhaiscript/rhai_rustler/tree/v0.1.1) (2022-11-07) 231 | 232 | [Full Changelog](https://github.com/rhaiscript/rhai_rustler/compare/v0.1.0...v0.1.1) 233 | 234 | - Set fail on invalid map property [\#8](https://github.com/rhaiscript/rhai_rustler/pull/8) ([fabriziosestito](https://github.com/fabriziosestito)) 235 | 236 | ## [v0.1.0](https://github.com/rhaiscript/rhai_rustler/tree/v0.1.0) (2022-11-04) 237 | 238 | [Full Changelog](https://github.com/rhaiscript/rhai_rustler/compare/7145150a1b8252fce8e3dd521d5107836a8e5132...v0.1.0) 239 | 240 | - Add filter example [\#7](https://github.com/rhaiscript/rhai_rustler/pull/7) ([fabriziosestito](https://github.com/fabriziosestito)) 241 | - Cleanup + README [\#6](https://github.com/rhaiscript/rhai_rustler/pull/6) ([fabriziosestito](https://github.com/fabriziosestito)) 242 | 243 | 244 | 245 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 246 | --------------------------------------------------------------------------------