Posted at

rustとmrubyでdslを作成してみよう

More than 3 years have passed since last update.

itamae-goの実装が面白そうだったので、ソースコードを調べていたんですが、

rustでもmrubyを組み込むcrateあったなーと思い、勉強ついでにマネをさせてもらいました。


用意するもの


  1. rust (1.10.0)


ソースコード

/tmp/madoka/homuraというディレクトリつくって、

そこに3つファイルを作るというあまり意味のないレシピ。


Cargo.toml


[dependencies]
mrusty = "*"



main.rs

#[macro_use]

extern crate mrusty;

use mrusty::*;

// ruby
static RUBY_RECIPE: &'static str = r#"
directory '/tmp/madoka/homura'

3.times do |i|
file "/tmp/madoka/homura/#{i}"
end
"
#;

#[allow(unused_must_use)]
#[allow(unused_variables)]
fn main() {
let mruby = Mruby::new();
let itamae = mruby.def_module("Itamae");
let recipe = mruby.def_class_under("Recipe", &itamae);
let context = mruby.def_class_under("Context", &recipe);

mruby.def_method(context.clone(), "directory", mrfn!(|mruby, slf: Value, v: (&str)| {
std::fs::create_dir_all(v)
.map(|_| mruby.bool(true))
.unwrap_or(mruby.bool(false))
}));

mruby.def_method(context, "file", mrfn!(|mruby, slf: Value, v: (&str)| {
std::fs::File::create(v)
.map(|_| mruby.bool(true))
.unwrap_or(mruby.bool(false))
}));

// itamae-goのように実装してみたら、mrustyだとうまくできなかったので、
// method_missingを利用。
mruby.run(r#"
class Wrapper
def initialize
@inst = Itamae::Recipe::Context.new
end

def method_missing(method, *args)
@inst.send(method, *args)
end
end
ITAMAE_CONTEXT = Wrapper.new
"#);

mruby.run(format!("ITAMAE_CONTEXT.instance_exec {{{}}}", RUBY_RECIPE).as_str());
}



まとめ

rustとmrubyでrubyのdslができたような気がします。

mrustyはrustのマクロ機能をいい感じにつかっていたので、とても勉強になりました。