概要
どうも、shypkです。
lua基盤にmagikaを導入した流れをお見せしたいと思います。
magikaとは
magikaはgoogleが開発したAIでfiletype判別できる技術らしいです。
javascript, rust, python などで対応していて、残念ながら現時点ではluaに対応はしていません。
mluaでラッピング
過去の経験から、luaで使いたい場合はラッピングしちゃえばなんとかなる気がします。
せっかくrust対応もしているので、速いだろうと信じて、rust版のmagikaをラッピングしたいと思います。
rustにはmluaというcrateがあり、luaで使える動的ライブラリを簡単に作れます。
luastring を受け取れるidentify_content_label関数を登録してみます。
use mlua::{Lua};
use mlua::prelude::*;
fn identify_content_label(_: &Lua, content: LuaString) -> LuaResult<String> { ... }
#[mlua::lua_module]
fn liblumagika(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("identify_content_label", lua.create_function(identify_content_label)?)?;
Ok(exports)
}
magikaを呼び出す。
あとはmagikaのcrateを呼び出すだけ。
こちらの説明によると
magika.identify_file_sync("src/lib.rs")?.info().label, "rust");
magika.identify_content_sync(&b"#!/bin/sh\necho hello"[..])?;
こういった使い方ができるらしいので、
今回はファイル内容を受け取って、そのlabelを返すという動作をしたいと思います。
上で繋いだidentify_content_labelの関数を内容を実装していきます。
そのコードがこちら。
fn identify_content_label(_: &Lua, content: LuaString) -> LuaResult<String> {
let ret: &str;
let _ = match Session::new() {
Ok(magika) => {
let result = magika.identify_content_sync(&*content.as_bytes());
match result {
Ok(result) => {
ret = result.content_type().unwrap_or(ContentType::Unknown).info().label;
},
Err(e) => {
ret = "Error";
println!("Error identifying content: {:?}", e);
},
}
},
Err(e) => {
ret = "Error";
println!("Error identifying content: {:?}", e);
}
};
Ok(ret.to_string())
}
cargo
cargoでビルドしたいので、Cargo.tomlに忘れずdependencyを追加しておきます。
[dependencies]
magika = { version = "0.1.0-rc.1" }
mlua = { version = "0.10.0", features = ["lua51","module"] }
mlua-sys = { version = "0.6.3", features = ["lua51","module"] }
(mlua::lua_module を使うには features=moduleが必要です。)
テスト
他のluaスクリプトで呼び出す準備ができました。
実際使っているコードは見せづらいので、ここではlua interpreterで簡単にテストしてみます。
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> package.cpath = package.cpath .. ";./target/release/?.so"
local lumagika = require("liblumagika")
local type = lumagika.identify_content_label("<html><body>some empty body</body><html>")
print(type)
html
> package.cpath = package.cpath .. ";./target/release/?.so"
local lumagika = require("liblumagika")
local type = lumagika.identify_content_label("<note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>")
print(type)
xml
ちゃんとhtml, xmlと出ました。
(都合上テキストだけのテストを表示していますが、pngなどbinaryもちゃんと判別できています。)
最後に
上で作られたlumagikaは下のgitにも隔離しています。
実際lumagikaで使われているコードはsasankaで公開しています。