11 releases
| 0.5.0 | Dec 31, 2023 |
|---|---|
| 0.3.2-alpha.1 | Aug 13, 2022 |
| 0.3.0 | Aug 13, 2022 |
| 0.2.1 | Aug 10, 2022 |
| 0.1.1-alpha.9 | Aug 10, 2022 |
#481 in Build Utils
66 downloads per month
Used in 2 crates
(via minutus)
7KB
135 lines
Minutus
Minutus is mruby binding for Rust, which enables you to treat mruby without writing painful boilerplates. Heavily inspired by Magnus.
By minutus, you can easily embed mruby in Rust, and create mrbgem by Rust.
Minutus also provides sensible type casting, and you can define typed functions on mruby values and wrap rust structs in mruby objects.
Embed mruby in Rust
You can embed mruby code in Rust code.
Add minutus to your crate's dependencies with link_mruby feature.
cargo add minutus --features link_mruby
Write code like:
// src/main.rs
// This enables you to call `some_method` in mruby world.
minutus::define_funcall!{
fn some_method(self, arr: Vec<i64>) -> i64;
}
fn main() {
let runtime = minutus::Evaluator::build();
// Define `some_method` in mruby world
runtime.evaluate(
"
def some_method(arr)
p arr
arr.reduce(&:+)
end
"
).unwrap();
// Capture mruby's main object
let main = runtime.evaluate("self").unwrap();
// Call `some_method` on main object.
// Arguments and return values are automatically type-casted according to `define_funcall!` definition.
let retval: i64 = main.some_method(vec![1,2,3,4]).unwrap();
println!("retval is {}", retval);
}
Then, you can run your code:
$ cargo run
[1, 2, 3, 4] // in mruby workd
retval is 10 // in rust world
If you want to use custom build_config.rb (e.g. for using mrbgems),
you have to write custom build.rs
Minutus provides a helper for this purpose. See examples/custom-mruby.
Create mrbgem by Rust
Install minutus-mrbgem-template and initialize mrbgem.
$ cargo install minutus-mrbgem-template
$ minutus-mrbgem-template mruby-example
$ tree mruby-example
mruby-example
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── Rakefile
├── mrbgem.rake
├── mrblib
│ └── mrb_example.rb
├── mruby-example.gem
├── src
│ ├── dummy.c
│ └── lib.rs
└── test
└── mrb_example.rb
Now, you can build and test mrbgem.
$ cd mruby-example && rake test
...
Total: 1456
OK: 1449
KO: 0
Crash: 0
Warning: 0
Skip: 7
Time: 0.06 seconds
Wrap Rust Structs in mruby Objects
You can wrap Rust's struct in mruby objects.
The following example defines TestMrbgem class in mruby,
which has class method new, and instance methods distance and name_with_prefix.
#[minutus::wrap(class_method = "new", method = "distance", method = "name_with_prefix")]
struct TestMrbgem {
x: i64,
y: i64,
name: String,
}
impl TestMrbgem {
#[minutus::class_method]
pub fn new(x: i64, y: i64, name: String) -> Self {
Self { x, y, name }
}
#[minutus::method]
pub fn distance(&self, other: &TestMrbgem) -> f64 {
(((self.x - other.x).pow(2) + (self.y - other.y).pow(2)) as f64).sqrt()
}
#[minutus::method]
pub fn name_with_prefix(&self, prefix: String) -> String {
[prefix, self.name.clone()].join("_")
}
}
Define typed functions on mruby values
Use define_funcall! macro.
minutus::define_funcall! {
fn inspect(self) -> String;
fn concat(self, other: Vec<&str>) -> Vec<String> => "+";
}
fn main() {
let runtime = minutus::Evaluator::build();
let mruby_array = runtime.evaluate("['aaa', 'bbb']").unwrap();
assert_eq!("[\"aaa\", \"bbb\"]", mruby_array.inspect().unwrap());
assert_eq!(vec![String::from("aaa"), String::from("bbb"), String::from("ccc")], mruby_array.concat(vec!["ccc"]).unwrap());
}
Type casting
See minutus/src/types for details.
| Rust type | mruby type |
|---|---|
i8, i16, i32, i64, isize |
Integer |
u8, u16, u32, u64, usize |
Integer |
f32, f64 |
Float |
String |
String |
Option<T> |
T or nil |
(T, U), (T, U, V), etc |
[T, U], [T, U, V], etc |
Vec<T> |
Array |
std::collections::HashMap<T, U> |
Hash |
minutus::types::RSymbol |
Symbol |
bool |
any object |
MrbData (structs marked by minutus::wrap) |
corresponding class |
Any value in mruby can be cast to Rust's bool.
Rust's bool cast to mruby's true or false.
Supported mruby versions
Following versions are supported:
You can also use mruby's master branch, but it is not tested.
If you need to use mruby versions not listed above (e.g., a specific tag or a fork),
you can do so via mruby_dir feature.
See "mruby_dir" section for details.
If the version is not specified on Cargo.toml, the latest supported stable version is used.
[dependencies]
# Use 3.3.0
minutus = "*"
# Use 3.2.0
minutus = { version = "*", features = ["mruby_3_2_0"] }
# Use master branch
minutus = { version = "*", features = ["mruby_master"] }
mruby_dir
When mruby_dir feature is specified, minutus uses your local directory as mruby.
# Cargo.toml
#
[dependencies]
minutus = { version = "*", features = ["mruby_dir"] }
Example
# POSIX-sh
#
cd /tmp
tag="240412a29080f775fabf97e56fc158b7249367e1"
curl -Lfo mruby.tgz "https://github.com/mruby/mruby/archive/${tag}.tar.gz"
tar -xf mruby.tgz
mv mruby-$tag mruby-src-dir
build:
Modify your project's Rakefile.
# Rakefile
#
# ...
# The value of `MINUTUS_MRUBY_DIR` is the path to the source code of mruby.
ENV["minutus_mruby_dir".upcase] = "/tmp/mruby-src-dir"
file :mruby do
#sh "git clone --depth=1 https://github.com/mruby/mruby.git"
#
unless Dir.exist?("mruby")
FileUtils.cp_r(ENV["minutus_mruby_dir".upcase], "mruby")
end
# ...
Modify mrbgem.rake:
# mrbgem.rake
#
MRuby::Gem::Specification.new('mruby-[your-project]') do |spec|
# ...
# If you encounter errors during the build process,
# you can try adding the `-vv` flag to Cargo for more detailed output.
sh "cd #{__dir__} && cargo build -vv --release"
# ...
Finally, run rake compile to build your project.
Supported Platforms
minutus primarily supports Unix-like systems.
If you encounter issues in a Windows MSVC environment,
try using msys2 and the *-pc-windows-gnu target.
Naming
Minutus is an antonym of Magnus, which means small.
Dependencies
~5–24MB
~309K SLoC