Files
hakorune/src/bid/plugin_box.rs
Moe Charm 5f6f946179 feat: 汎用プラグインBox生成システム実装とnyash.toml v2対応準備
- GenericPluginBoxを実装し、任意のプラグインBoxを動的に生成可能に
- FileBox決め打ちコードを削除(設計思想違反の解消)
- CURRENT_TASK.mdを更新し、nyash.toml v2対応の必要性を明確化
- 問題: プラグインテスターとNyash本体が古い単一Box型形式のまま

次のステップ:
1. nyash.tomlをv2形式(マルチBox型)に更新
2. プラグインテスターをv2対応に
3. Nyash本体のレジストリをv2対応に

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-19 03:48:44 +09:00

201 lines
7.6 KiB
Rust
Raw 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.

use crate::bid::{BidError, BidResult, LoadedPlugin};
use crate::bid::tlv::{TlvEncoder, TlvDecoder};
use crate::bid::types::BidTag;
use crate::bid::metadata::{NyashMethodInfo, NyashPluginInfo};
use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase};
/// Minimal plugin-backed instance that manages birth/fini lifecycle
pub struct PluginBoxInstance<'a> {
pub plugin: &'a LoadedPlugin,
pub instance_id: u32,
}
impl<'a> PluginBoxInstance<'a> {
/// Create a new instance by invoking METHOD_BIRTH (0)
pub fn birth(plugin: &'a LoadedPlugin) -> BidResult<Self> {
let mut out = Vec::new();
plugin.handle.invoke(plugin.type_id, 0, 0, &[], &mut out)?;
// Expect TLV encoding of handle or instance id; current prototype returns raw u32
let instance_id = if out.len() == 4 {
u32::from_le_bytes([out[0], out[1], out[2], out[3]])
} else {
// Try to decode TLV handle (future-proof)
return Err(BidError::InvalidArgs);
};
Ok(Self { plugin, instance_id })
}
// Method IDs are fixed for FileBox in BID-1 prototype:
// 1=open, 2=read, 3=write, 4=close
pub fn open(&self, path: &str, mode: &str) -> BidResult<()> {
let method = 1; // open
let mut enc = TlvEncoder::new();
enc.encode_string(path)?;
enc.encode_string(mode)?;
let args = enc.finish();
let mut out = Vec::new();
self.plugin.handle.invoke(self.plugin.type_id, method, self.instance_id, &args, &mut out)?;
Ok(())
}
pub fn write(&self, data: &[u8]) -> BidResult<i32> {
let method = 3; // write
let mut enc = TlvEncoder::new();
enc.encode_bytes(data)?;
let args = enc.finish();
let mut out = Vec::new();
self.plugin.handle.invoke(self.plugin.type_id, method, self.instance_id, &args, &mut out)?;
let mut dec = TlvDecoder::new(&out)?;
if let Some((tag, payload)) = dec.decode_next()? {
if tag != BidTag::I32 { return Err(BidError::InvalidType); }
return Ok(TlvDecoder::decode_i32(payload)?);
}
Err(BidError::PluginError)
}
pub fn read(&self, size: usize) -> BidResult<Vec<u8>> {
let method = 2; // read
let mut enc = TlvEncoder::new();
enc.encode_i32(size as i32)?;
let args = enc.finish();
let mut out = Vec::new();
self.plugin.handle.invoke(self.plugin.type_id, method, self.instance_id, &args, &mut out)?;
let mut dec = TlvDecoder::new(&out)?;
if let Some((tag, payload)) = dec.decode_next()? {
if tag != BidTag::Bytes { return Err(BidError::InvalidType); }
return Ok(payload.to_vec());
}
Err(BidError::PluginError)
}
pub fn close(&self) -> BidResult<()> {
let method = 4; // close
let mut enc = TlvEncoder::new();
enc.encode_void()?;
let args = enc.finish();
let mut out = Vec::new();
self.plugin.handle.invoke(self.plugin.type_id, method, self.instance_id, &args, &mut out)?;
Ok(())
}
}
impl<'a> Drop for PluginBoxInstance<'a> {
fn drop(&mut self) {
// METHOD_FINI = u32::MAX
let _ = self.plugin.handle.invoke(
self.plugin.type_id,
u32::MAX,
self.instance_id,
&[],
&mut Vec::new(),
);
}
}
/// NyashBox implementation wrapping a BID plugin FileBox instance
pub struct PluginFileBox {
base: BoxBase,
inner: PluginBoxInstance<'static>,
path: String,
}
impl PluginFileBox {
pub fn new(plugin: &'static LoadedPlugin, path: String) -> BidResult<Self> {
let inst = PluginBoxInstance::birth(plugin)?;
// Open with read-write by default (compat with built-in)
inst.open(&path, "rw")?;
Ok(Self { base: BoxBase::new(), inner: inst, path })
}
/// 引数なしでFileBoxインスタンスを作成birth専用
pub fn birth(plugin: &'static LoadedPlugin) -> BidResult<Self> {
let inst = PluginBoxInstance::birth(plugin)?;
// パスなしでインスタンス作成後でopenで指定
Ok(Self { base: BoxBase::new(), inner: inst, path: String::new() })
}
pub fn read_bytes(&self, size: usize) -> BidResult<Vec<u8>> { self.inner.read(size) }
pub fn write_bytes(&self, data: &[u8]) -> BidResult<i32> { self.inner.write(data) }
pub fn close(&self) -> BidResult<()> { self.inner.close() }
/// 汎用メソッド呼び出し(動的ディスパッチ)
pub fn call_method(&self, method_name: &str, args: &[u8]) -> BidResult<Vec<u8>> {
eprintln!("🔍 call_method: method_name='{}', args_len={}", method_name, args.len());
// プラグインからメソッドIDを動的取得
match self.inner.plugin.find_method(method_name) {
Ok(Some((method_id, signature))) => {
eprintln!("🔍 Found method '{}': ID={}, signature=0x{:08X}", method_name, method_id, signature);
let mut out = Vec::new();
match self.inner.plugin.handle.invoke(
self.inner.plugin.type_id,
method_id,
self.inner.instance_id,
args,
&mut out
) {
Ok(()) => {
eprintln!("🔍 Plugin invoke succeeded, output_len={}", out.len());
Ok(out)
}
Err(e) => {
eprintln!("🔍 Plugin invoke failed: {:?}", e);
Err(e)
}
}
}
Ok(None) => {
eprintln!("🔍 Method '{}' not found in plugin", method_name);
Err(BidError::InvalidArgs) // メソッドが見つからない
}
Err(e) => {
eprintln!("🔍 Error looking up method '{}': {:?}", method_name, e);
Err(e)
}
}
}
/// プラグインのメソッド一覧を取得
pub fn get_available_methods(&self) -> BidResult<Vec<(u32, String, u32)>> {
self.inner.plugin.get_methods()
}
}
impl BoxCore for PluginFileBox {
fn box_id(&self) -> u64 { self.base.id }
fn parent_type_id(&self) -> Option<std::any::TypeId> { self.base.parent_type_id }
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "FileBox({}) [plugin]", self.path)
}
fn as_any(&self) -> &dyn std::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
}
impl NyashBox for PluginFileBox {
fn to_string_box(&self) -> StringBox { StringBox::new(format!("FileBox({})", self.path)) }
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(of) = other.as_any().downcast_ref::<PluginFileBox>() {
BoolBox::new(self.path == of.path)
} else { BoolBox::new(false) }
}
fn clone_box(&self) -> Box<dyn NyashBox> {
// Create a new plugin-backed instance to the same path
if let Some(reg) = crate::bid::registry::global() {
if let Some(plugin) = reg.get_by_name("FileBox") {
if let Ok(newb) = PluginFileBox::new(plugin, self.path.clone()) {
return Box::new(newb);
}
}
}
Box::new(StringBox::new("<plugin clone failed>"))
}
fn share_box(&self) -> Box<dyn NyashBox> { self.clone_box() }
}
impl std::fmt::Debug for PluginFileBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PluginFileBox(path={})", self.path)
}
}