Files
hakorune/docs/reference/architecture/plugin-migration-guide-v2.md

249 lines
7.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 📦 Nyash ビルトインBox → プラグイン化移行ガイド v2
## 🎯 概要
NyashのビルトインBoxをプラグイン化し、コアを軽量化します。
FileBoxプラグインの成功例を詳しく解説しながら、移行方法を説明します。
## 🔑 重要な概念nyash.tomlの型定義システム
### 型変換の仕組み
nyash.tomlでは、Nyash側とプラグイン側の型変換を明示的に定義します
```toml
# FileBoxの例
[plugins.FileBox.methods]
# writeメソッドNyashのstringをプラグインではbytesとして扱う
write = { args = [{ from = "string", to = "bytes" }] }
# openメソッド2つのstring引数型変換なし
open = { args = [
{ name = "path", from = "string", to = "string" },
{ name = "mode", from = "string", to = "string" }
] }
```
### from/toの意味
- **from**: Nyash側の型ユーザーが渡す型
- **to**: プラグイン側で受け取る型TLVエンコーディング
### TLVタグとの対応
プラグインはTLVType-Length-Value形式でデータを受け取ります
- `to = "i32"` → TLV tag=232ビット整数
- `to = "string"` → TLV tag=6UTF-8文字列
- `to = "bytes"` → TLV tag=7バイト配列
## 📋 移行対象Box一覧優先順位順
### 🌐 Phase 1: ネットワーク系(最優先・最も簡単)
既にスタブ実装があり、reqwest依存を追加するだけで完成します。
#### HttpClientBox
```toml
[plugins.HttpClientBox.methods]
# シンプルなGETリクエスト
get = {
args = [{ from = "string", to = "string" }], # URL
returns = "string" # レスポンスボディ
}
# POSTリクエストボディ付き
post = {
args = [
{ from = "string", to = "string" }, # URL
{ from = "string", to = "bytes" } # ボディ(バイナリ対応)
],
returns = "string"
}
# 詳細なリクエスト(ヘッダー等を含む)
request = {
args = [
{ from = "string", to = "string" }, # メソッドGET/POST等
{ from = "string", to = "string" }, # URL
{ from = "map", to = "map" } # オプションheaders, timeout等
],
returns = "map" # { status: i32, body: string, headers: map }
}
```
### 🖼️ Phase 2: GUI系プラットフォーム依存
EguiBoxは既にfeature分離されているので参考になります。
### 🎵 Phase 3: 特殊用途系(独立性高い)
TimerBox、QRBox等は単機能で実装しやすいです。
## 🔧 実装ガイドFileBoxを例に
### 1. プラグイン側での型受け取り例
```rust
// nyash.toml: write = { args = [{ from = "string", to = "bytes" }] }
METHOD_WRITE => {
// TLVでbytesとして受け取る
let data = tlv_parse_bytes(args)?; // Vec<u8>として取得
// ファイルに書き込み
match file.write(&data) {
Ok(n) => {
file.flush()?; // 重要:フラッシュを忘れずに!
// 書き込んだバイト数を返すTLV i32
write_tlv_i32(n as i32, result, result_len)
}
Err(_) => NYB_E_PLUGIN_ERROR
}
}
```
### 2. 複数引数の解析例
```rust
// nyash.toml: open = { args = [{ from = "string", to = "string" }, { from = "string", to = "string" }] }
METHOD_OPEN => {
// 2つのstring引数を解析
let (path, mode) = tlv_parse_two_strings(args)?;
// ファイルを開く
let file = match mode.as_str() {
"r" => File::open(&path)?,
"w" => File::create(&path)?,
"a" => OpenOptions::new().append(true).open(&path)?,
_ => return NYB_E_INVALID_ARGS
};
// 成功時はVoidを返す
write_tlv_void(result, result_len)
}
```
### 3. 引数なしメソッドの例
```rust
// nyash.toml: read = { args = [] }
METHOD_READ => {
// 引数なし - ファイル全体を読む
file.seek(SeekFrom::Start(0))?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
// bytesとして返す
write_tlv_bytes(&buf, result, result_len)
}
```
## 📝 HttpClientBox実装の具体例
```rust
// HttpClientBoxプラグインの実装イメージ
use reqwest::blocking::Client;
METHOD_GET => {
// URLを解析
let url = tlv_parse_string(args)?;
// HTTPリクエスト実行
let client = Client::new();
let response = client.get(&url).send()?;
let body = response.text()?;
// 文字列として返す
write_tlv_string(&body, result, result_len)
}
METHOD_POST => {
// URL と ボディを解析
let (url, body_bytes) = tlv_parse_string_and_bytes(args)?;
// POSTリクエスト
let client = Client::new();
let response = client.post(&url)
.body(body_bytes)
.send()?;
let body = response.text()?;
write_tlv_string(&body, result, result_len)
}
```
## 💡 実装のコツとよくある間違い
### ✅ 正しいnyash.toml
```toml
# 引数の型変換を明示
write = { args = [{ from = "string", to = "bytes" }] }
# 戻り値の型も指定可能
exists = { args = [], returns = "bool" }
```
### ❌ よくある間違い
```toml
# 間違い:型情報がない
write = { args = ["string"] } # ❌ from/toが必要
# 間違い:不要なフィールド
get = { args = [{ type = "string" }] } # ❌ typeではなくfrom/to
```
### メモリ管理の注意点
1. 文字列は必ずCString/CStr経由で変換
2. プラグイン側でallocしたメモリはプラグイン側でfree
3. ホスト側のVtableを使ってログ出力
### エラーハンドリング
```rust
// パニックをFFI境界で止める
let result = std::panic::catch_unwind(|| {
// 実際の処理
});
match result {
Ok(val) => val,
Err(_) => NYB_E_PLUGIN_ERROR
}
```
## 🧪 テスト方法
### 1. プラグインビルド
```bash
cd plugins/nyash-http-plugin
cargo build --release
```
### 2. plugin-testerで診断
```bash
cd ../../tools/plugin-tester
./target/release/plugin-tester ../../plugins/nyash-http-plugin/target/release/libnyash_http_plugin.so
# 期待される出力:
# Plugin Information:
# Box Type: HttpClientBox (ID: 20)
# Methods: 5
# - birth [ID: 0] (constructor)
# - get, post, put, delete
# - fini [ID: 4294967295] (destructor)
```
### 3. Nyashで実行
```nyash
// test_http.hako
local http = new HttpClientBox()
local response = http.get("https://api.example.com/data")
print(response)
```
## 📚 参考資料
- **FileBoxプラグイン完全実装**: `plugins/nyash-filebox-plugin/src/lib.rs`
- **TLVエンコーディング仕様**: `docs/説明書/reference/plugin-system/ffi-abi-specification.md`
- **nyash.toml設定例**: プロジェクトルートの`nyash.toml`
## 🎯 成功の秘訣
1. **FileBoxを完全に理解してから始める** - コピペベースで改造
2. **nyash.tomlの型定義を正確に** - from/toを明示
3. **TLVの理解** - tag=6(string), tag=7(bytes)の違い
4. **plugin-testerで早期検証** - 問題を早期発見
---
質問があれば、FileBoxの実装を参考にしてください。
すべての答えがそこにあります!